expect
是基于Tcl (tool command language)
语言开发的,主要应用于自动化交互式操作的场景,借助expect
处理交互的命令,可以将交互过程如:ssh
登录,ftp
登录等写在一个脚本上,使之自动化完成。尤其适用于需要对多台服务器执行相同操作的环境中,可以大大提高系统管理人员的工作效率
常用指令
spawn
:交互程序开始后面跟命令或者指定程序(在壳内启动这个进程)expect
:获取匹配信息匹配成功则执行expect
后面的程序动作(检测由壳内进程发出的特定交互指令反馈字符串后向下执行)send
:用于向进程发送字符串(从壳外向壳内进程发送一条字符串,换行符为确认结束)interact
:运行用户交互exp_continue
:在expect
中多次匹配就需要用到send_user
:用来打印输出,相当于shell
中的echo
exit
:退出expect
脚本eof
:expect
执行结束,退出set
:定义变量puts
:输出变量
命令实例
简单例子
先看一个简单例子,就是自动登录到网络设备msr3600
cat /root/expect/ssh.tcl
#!/usr/bin/expect
spawn ssh xxx@192.168.1.2
expect "password"
send "xxx"
expect ">"
send "quit\n"
expect eof
# 执行
expect ssh.tcl
可以自动登录然后再退出
send命令
send
命令接收一个字符串参数,并将该参数发送到进程(前提是先使用spawn
开启进程)
上面的示例中,有2处使用了send
其实就是spawn
打开了一次ssh
连接以后,会要求我们输入登录密码,
第一个send
就是将密码发送到spawn
的进程中。
第二个send
就是退出ssh
的意思。
expect
expect
命令和send
命令正好相反,expect
通常是用来等待一个进程的反馈。
expect
可以接收一个字符串参数,也可以接收正则表达式参数。
比如root
登录之后,界面会输出一个#
,那么expect
此时匹配的是这个#
。
ssh
登录后,一般shell
会返回一个password:
的输出,那么此时可以匹配password
的字符。
而如果我们没有通过spawn
开启一个ssh
或者类似的进程,而是直接在expect
程序里面expect
一个字符串的时候,会怎么样?
#!/bin/bash/expect
expect "hello" { send "hello world\n" }
这时候,当我们执行脚本的时候,会发现,除非在键盘上敲入hello
然后确认,才会输出hello world
expect
用法是匹配到指定的字符时,执行指定的动作。匹配有2种匹配方式:
单一分支匹配
类似与上面的例子:
expect "hello" { send "hello world\n" }
单一匹配就是只有一种匹配情况。
有点类似于普通编程语言的if
语句,只有一个条件的情况。
多分支匹配模式
#!/usr/bin/expect
set timeout 5
expect {
"hello" {send "hello world\n"}
"hi" {send "hi world"}
"bye" {send "bye world"}
}
我们发现,expect
语言都是用{}
来做代码分割和代码块分割的。
spawn
最开始的ssh
案例里面,我们使用spawn
开启了一个ssh
进程,然后使用send
输入密码。
我们在加多写命令,查看登录设备的version
:
#!/usr/bin/expect
spawn ssh xxx@x.x.x.x
expect "password"
send "xxx\n"
expect ">"
send "system-view\n"
expect "]"
send "display version\n"
send "exit\n"
send "quit\n"
expect eof
spawn
开启一个ssh
以后,会进入到网络设备的shell
登录环境下,
这时候向进程发送一个display version
这样的字符串,网络设备就能够识别这是一个有意义的指令,并返回指令的结果。
interact
上述的例子都是自动完成了一些动作。
有时候我们希望停留在界面,等待人工操作的情况。
这时候,我们可以用interact
指令,来等待人工干预
#!/usr/bin/expect
spawn ssh xxx@x.x.x.x
expect "password"
send "xxx\n"
expect ">"
send "system-view\n"
expect "]"
interact
该例子执行完system-view
以后,会停留在expect
打开的ssh
界面,等待人工操作。
set
该指令是用来设置变量值。比如:
#!/usr/bin/expect
set uname xxx
set pwd yyy
spawn ssh ${uname}@x.x.x.x
expect "password"
send "${pwd}\n"
expect ">"
send "quit\n"
expect eof
传参
很多场景下,我们写一个脚本都是要传递参数的。
expect
也不例外。
expect
有2个内置变量:argc
和argv
。
argc
表示参数的数量,类似于普通shell
脚本的#
。
argv
则可以给自身传递一个参数,以便脚本取出指定位置的参数。例如:
#!/usr/bin/expect
set uname [lindex $argv 0]
set pwd [lindex $argv 1]
puts "$argc"
puts "$argv0"
spawn ssh ${uname}@x.x.x.x
expect "password"
send "${pwd}\n"
expect ">"
send "quit\n"
expect eof
我们可以在运行脚本时传入2个参数
expect ssh4.tcl xxx yyy
expect
还有一个非常重要的内置变量,arvg0
。
argv
是存储了所有传递进来的参数的变量。
而argv0
则是脚本的名称。这个和shell脚本的$0
一个意思。
脚本里面,出现了一个put指令,这个指令是向标准输出输出内容。
从结果看,我们传递了2个参数,puts
指令将$argc
输出到了屏幕,值是2,表示有2个参数。
也将$argv0
脚本名称输出到了屏幕。
同时,通过lindex $argv int
,获取到指定位置的参数值。
运算
incr
增量。一般用在数学计算的时候。
语法为:incr arg {step}
,arg
时要增加的参数名,step
是增量值,可以不指定,不指定为1。
puts "------incr-------"
set x 10
puts $x
incr x 5
puts $x
# 结果如下:
------incr-------
10
15
其实这里的incr x 5
也相当于set x [expr $x + 5]
[]符号
# 访问数组
puts [lindex $argv 1]
# 数学计算的时候
puts [expr $x + 5]
# 分割字符串的时候
set ss "aa,bb,cc,dd"
puts [split $ss ","]
数组
所有传递的参数,被存放在argv
中。
其实argv
就是数组
数组的定义
数组的定义,需要结合set的指令
set j "a b c d"
数组的所有元素之间,需要用空格隔开。
数组访问
我们使用[lindex $argv 0]
的方法,获取了第0个参数。这个正也是数组的访问方法:
#!/usr/bin/expect
set timeout 5
set j "a b c d e"
puts "[lindex $j 2]"
# 遍历数组,可以使用foreach的方法:
set j "a b c d e"
foreach jj $j {
puts "$jj"
}
for循环
puts "------递增-----"
for {set i 0} { $i < 5 } { incr i } {
puts "$i"
}
puts "------递减-----"
for {set k 5} { $k > 0 } { incr k -1 } {
puts "$k"
}
while循环
puts "------while递增-----"
set m 0
while {$m < 5} {
puts "$m"
incr m 2
}
foreach写法
#!/usr/bin/expect
set timeout 5
puts "------遍历argv--------"
foreach arg $argv {
puts "$arg"
}
# 还可以这么遍历
#!/usr/bin/expect
set timeout 5
for {set y 0 } { $y < $argc} {incr y} {
puts "arg $y: [lindex $argv $y]"
}
shell调用expect
cat ssh.sh
#!/usr/bin/bash
ip=$1
pwd=$2
expect << EOF
set timeout 10
spawn ssh ylzw@${ip}
expect "password"
send "${pwd}\n"
expect ">"
send "system-view\n"
expect "]"
send "display version\n"
send "exit\n"
send "quit\n"
expect eof
EOF
echo "end!"
留言