expect学习笔记及实例详解

2023-05-24,,

expect学习笔记及实例详解引用自:http://wenku.baidu.com/view/b65e103610661ed9ad51f374.html
1. expect 是基于tcl 演变而来的,所以很多语法和tcl 类似,基本的语法如下所示:1.1 首行加上/usr/bin/expect1.2 spawn: 后面加上需要执行的shell 命令,比如说spawn sudo touch testfile1.3 expect: 只有spawn 执行的命令结果才会被expect 捕捉到,因为spawn 会启动一个进程,只有这个进程的相关信息才会被捕捉到,主要包括:标准输入的提示信息,eof 和timeout。1.4 send 和send_user:send 会将expect 脚本中需要的信息发送给spawn 启动的那个进程,而send_user 只是回显用户发出的信息,类似于shell 中的echo 而已。
2. 一个小例子,用于linux 下账户的建立:filename: account.sh,可以使用./account.sh newaccout 来执行;1 #!/usr/bin/expect23 set passwd "mypasswd"4 set timeout 6056 if {$argc != 1} {7 send "usage ./account.sh \$newaccount\n"8 exit9 }1011 set user [lindex $argv [expr $argc-1]]1213 spawn sudo useradd -s /bin/bash -g mygroup -m $user1415 expect {16 "assword" {17 send_user "sudo now\n"18 send "$passwd\n"19 exp_continue20 }21 eof22 {23 send_user "eof\n"24 }25 }2627 spawn sudo passwd $user28 expect {29 "assword" {30 send "$passwd\n"31 exp_continue32 }33 eof34 {35 send_user "eof"36 }37 }3839 spawn sudo smbpasswd -a $user40 expect {41 "assword" {42 send "$passwd\n"43 exp_continue44 }45 eof46 {47 send_user "eof"48 }49 }
3. 注意点:
第3 行: 对变量赋值的方法第4 行: 默认情况下,timeout 是10 秒;第6 行: 参数的数目可以用$argc 得到;第11 行:参数存在$argv 当中,比如取第一个参数就是[lindex $argv 0];并且如果需要计算的话必须用expr,如计算2-1,则必须用[expr 2-1]第13 行:用spawn 来执行一条shell 命令,shell 命令根据具体情况可自行调整;有文章说sudo 要加-S,经过实际测试,无需加-S 亦可;第15 行:一般情况下,如果连续做两个expect,那么实际上是串行执行的,用。expect 与“{ ”之间直接必须有空格或则TAB间隔,否则会出麻烦,会报错invalid command name "expect{" 例子中的结构则是并行执行的,主要是看匹配到了哪一个;在这个例子中,如果你写成串行的话,即expect "assword"send "$passwd\n"expect eofsend_user "eof"那么第一次将会正确运行,因为第一次sudo 时需要密码;但是第二次运行时由于密码已经输过(默认情况下sudo 密码再次输入时间为5 分钟),则不会提示用户去输入,所以第一个expect 将无法匹配到assword,而且必须注意的是如果是spawn 命令出现交互式提问的但是expect 匹配不上的话,那么程序会按照timeout的设置进行等待;可是如果spawn 直接发出了eof 也就是本例的情况,那么expect"assword"将不会等待,而直接去执行expect eof。这时就会报expect: spawn id exp6 not open,因为没有spawn 在执行,后面的expect 脚本也将会因为这个原因而不再执行;所以对于类似sudo 这种命令分支不定的情况,最好是使用并行的方式进行处理;第17 行:仅仅是一个用户提示而已,可以删除;第18 行:向spawn 进程发送password;第19 行:使得spawn 进程在匹配到一个后再去匹配接下来的交互提示;第21 行:eof 是必须去匹配的,在spawn 进程结束后会向expect 发送eof;如果不去匹配,有时也能运行,比如sleep 多少秒后再去spawn 下一个命令,但是不要依赖这种行为,很有可能今天还可以,明天就不能用了;
4. 其他下面这个例子比较特殊,在整个过程中就不能expect eof 了:1 #!/usr/bin/expect23 set timeout 304 spawn ssh 10.192.224.2245 expect "password:"6 send "mypassword\n"7 expect "*$"8 send "mkdir tmpdir\n" #远程执行命令用send发送,不用spawn9 expect "*$" #注意这个地方,要与操作系统上环境变量PS1相匹配,尤其是有PS1有空格的情况下,一定在expct "*$ "把空格加上,加不上你就完蛋了。我试过。这个例子实际上是通过ssh 去登录远程机器,并且在远程机器上创佳一个目录,我们看到在我们输入密码后并没有去expect eof,这是因为ssh 这个spawn 并没有结束,而且手动操作时ssh 实际上也不会自己结束除非你exit;所以你只能expect bash 的提示符,当然也可以是机器名等,这样才可以在远程创建一个目录。注意,请不要用spawn mkdir tmpdir,这样会使得上一个spawn 即ssh 结束,那么你的tmpdir 将在本机建立。当然实际情况下可能会要你确认ssh key,可以通过并行的expect 进行处理,不多赘述。
5. 觉得bash 很多情况下已经很强大,所以可能用expect 只需要掌握这些就好了,其他的如果用到可以再去google 了。
源代码图片:

6 \实例:下面这个脚本是完成对单个服务器scp任务。

10: spawn scp $src_file $username@$host:$dest_file

 11: expect {
7: cat $list_file | while    read line
 13:     ./expect_scp $host_ip $username $password $src_file $dest_file
 15: done

参考代码图片如下:

很简单的代码,指定了3个参数:列表文件的位置、本地源文件路径、远程主机目标文件路径。需要说明的是其中的列表文件指定了远程主机ip、用户名、密码,这些信息需要写成以下的格式:

IP username password

中间用空格或tab键来分隔,多台主机的信息需要写多行内容。

这样就指定了两台远程主机的信息。注意,如果远程主机密码中有“$”、“#”这类特殊字符的话,在编写列表文件时就需要在这些特殊字符前加上转义字符,否则expect在执行时会输入错误的密码。

对于这个shell脚本,保存为batch_scp.sh文件,与刚才保存的expect_scp文件和列表文件(就定义为hosts.list文件吧)放到同一目录下,执行时按照以下方式输入命令就可以了:

1.jpg

2.jpg

3.jpg

1.jpg

2.jpg

11.jpg

12.jpg

13.jpg

14.jpg