内容目录

expect是基于Tcl (tool command language)语言开发的,主要应用于自动化交互式操作的场景,借助expect处理交互的命令,可以将交互过程如:ssh登录,ftp登录等写在一个脚本上,使之自动化完成。尤其适用于需要对多台服务器执行相同操作的环境中,可以大大提高系统管理人员的工作效率

常用指令

  • spawn:交互程序开始后面跟命令或者指定程序(在壳内启动这个进程)
  • expect:获取匹配信息匹配成功则执行expect后面的程序动作(检测由壳内进程发出的特定交互指令反馈字符串后向下执行)
  • send:用于向进程发送字符串(从壳外向壳内进程发送一条字符串,换行符为确认结束)
  • interact:运行用户交互
  • exp_continue:在expect中多次匹配就需要用到
  • send_user:用来打印输出,相当于shell中的echo
  • exit:退出expect脚本
  • eofexpect执行结束,退出
  • 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个内置变量:argcargv
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!"
最后修改日期: 2024年11月10日

留言

撰写回覆或留言

发布留言必须填写的电子邮件地址不会公开。