使用expect实现自动交互,shell命令行自动输入

背景

有需求,在容许命令或者脚本跳出交互行,须要进行内容输入,但须要人手动输入,不是很方便,此时能够经过expect来实现自动互动交互。javascript

expect是一个自动交互功能的工具,能够知足代替咱们实际工做中须要从终端手动输入某些内容来使得程序或命令继续运行的目的。如安装软件是时的一些提示,ssh远程主机执行命令时须要屡次输入密码的状况。java

安装expect

  • 安装依赖:yum install tcl -y
  • 安装expect:Centos系统yum install expect -y或Ubuntu系统apt-get install expect -y

一些基本的expect命令

  • spawn :启动新进程,用于执行shell命令;
  • expect :从发起交互的命令的进程接受字符串,用于匹配咱们预想的字符串;
  • send :用于向发起交互的命令的进程发送字符串;
  • interact:容许用户交互,即此命令后,交互将不会由expect进行,将交回给用户;

示例

#!/usr/bin/expect set timeout 30 set host "192.168.200.221" set username "root" set password "123456" spawn ssh $username@$host ls expect "password" {send "$password\r"} expect eof interact

#!/usr/bin/expect: 表示使用expect来解释该脚本。mysql

set timeout 30: 表示设置超时时间,这里是表示超时时间为30秒,默认为10秒,用于执行shell命令的时间,若是执行的shell命令时间较长(如传输文件),则须要设置长一点。nginx

set username "root" : 表示设置并定义了变量username,变量值为"root"。sql

spawn ssh username@username@host ls: 表示使用spawn来执行ssh $username@$host ls 命令,该命令只有在expect环境里才能执行,因此直接在命令行输入或没有安装expect则会报错,它的主要功能是给它后面的shell命令运行进程加了个壳,进行传递交互的内容,注意,若是用引号将变量引发,将可能致使错误extra characters after close-quote...,若是执行的命令须要用到引号,使用双引号,并使用\转义,但只适用于命令中只有一对引号的状况,若是出现多对引号,将会出现一些奇怪的错误,暂时不知道如何解决。shell

ssh -l root 192.168.200.118 'mysql -uroot -p123456 -e "show datavases;"' 命令。只能先登陆目标主机,再匹配root@ubuntu:~#,send发送命令。ubuntu

#!/usr/bin/expect -f set timeout -1 spawn ssh root@192.168.200.118 expect -re "password" { send "userpwd123\r" } expect -re ":~#" { send "mysql -uroot -p123456\r" } expect -re "mysql>" { send "show databases;\r" } expect -re "mysql>" { exit } expect eof

expect "password": 表示从spawn执行的命令的进程里接受字符串,通常是弹出终端的交互行的标准输入提示信息,如须要你肯定时的(yes/no?),须要你输入密码的(...password:)。这里由于ssh命令的交互内容是叫你输入密码,交互提示的内容有password,因此这里匹配password。须要注意的是,expect接受的是spawn执行的命令进程中可能出现的字符串,若是你的spawn执行的命令在执行完以后直接没有进程了,那expect也将不能匹配到任何的字符串,如spawn简单的执行ls等命令,这也说明expect多用于须要执行链接的场景。数组

send "$password\r": 表示当expect命令匹配成功,就把$password发送给spawn执行的命令的进程,完成交互,至关于手动输入$password,这里的\r表明回车,也可使用\n,记得加上\r或\n,不然脚本将可能会卡死。bash

expect eof: 表示结束expect,读取到文件结束符 ,当spawn发送指令到终端执行时在返回时被expect捕捉时,在起始会有一个eof,就比如在shell中 cat >>file <<EOF... EOF同样,在结束时也要有eof;expect eof有时间限制,即咱们设置的超时时间,默认10秒,不过可能出现的问题是,若是是在传输一个大文件,可能在文件还没传输完成便断开了命令执行,此时须要设置超时时间长一点或 set timeout -1,或将expect eof改为expect -timeout -1 eofmarkdown

interact: 执行完命令后,控制权交互控制台,此时再有交互,expect将不会进行交互,须要手动进行输入内容交互。若是没有这句,在须要交互的ssh命令执行完毕后将会退出远程,而不是继续保持在远程。

expect参数

  • $argc:表示命令行参数个数
  • [lindex $argv n]:表示index为n的参数(index从0开始计算),index的区间为左闭右开,如[lindex $argv 0]表明命令行输入的第一个参数,[lindex $argv 0 3] 表明命令行输入的第一到第三个参数 。

示例

#!/usr/bin/expect set host [lindex $argv 0] set username [lindex $argv 1] set num $argc if { num < 3 } { ... }
  • 将第一个命令行参数赋值给变量host,将第二个命令行参数赋值给变量username,将总参数个数赋值给变量num。

expect流程控制

if语句:

if {条件1} { expect { "(yes/no)" { send "yes\r" expect "*assword:" {send "$password\r"} } "password" { send "$password\r" } } else { puts "Expect was timeout" send_user "Expect was timeout" }

expect {}: 多行指望,从上往下匹配,匹配成功里面的哪一条,将执行与之的send命令,注意,这里面的匹配字符串只会执行一个,即匹配到的那个,其他的将不会执行,若是想匹配这句命令执行成功后(如登陆成功后等待输入的root@ubuntu:~#)的其余字符,须要另起一个expect命令,并保证不在expect{}里面。

puts与send_user: 打印信息,相似echo

其余:

  • 判断条件用{}包含
  • 花括号与花括号,和括号与控制语句之间须要有空格,不然会报错expect:extra characters after close-brace
  • if右边要有左花括号,else左边要有右花括号,不能单独一行

for语句:

for {set i 0} {$i < 10} {incr i} { puts "I inside first loop: $i" }

while语句:

set i 0 while {$i < 10} { puts "I inside third loop: $i" incr i puts "I after incr: $i" }

incr: 递增运算符 incr i ,相似++

switch语句:

switch--$var { 0 { 语句块 } 1 { 语句块 } ... }

函数定义和调用:

使用proc定义函数,使用时输入函数名和参数调用

proc test_exp {argv1 argv2} { puts "hello:$argv1" } test_exp 参数1 参数2

expect数组:

set arr(n) "hello" # 赋值,arr为数组名 set arr(1) "first" $arr(1) # 引用 array size arr # 查看数组大小 注意:若是是shell中插入的一段expect中想使用数组,须要转义\$,或<<\EOF...EOF

其余的一些内容

  • 使用正则匹配:使用 -re选项,expect -re "\\\[(.*)]" 其中[在expect shell 正则中都有特殊意义,所以要\三次 ,若是spawn执行的命令不能匹配通配符*,须要在spawn 后加 bash -c。

  • expect -i选项:已交互的方式运行expect。

  • expect -D选项:交互式的调试器,相似gdb。

  • expect -c选项:可执行命令的前置符,expect命令可在命令行执行,该选项-c后的命令须要引号引发来,引号内多个命令分号隔开,可以使用屡次-c选项,空格隔开。

  • expect -f选项:常见于文件第一行,即#!/usr/bin/expect -f ,指定expect读取的expect命令文件,可选项,该选项会将文件一次性所有读取入内存,加上-f选项能够为执行expect提供更多参数。

  • expect -b选项:相似-f选项,只是每次只读取一行,便可以逐行的执行expect。

  • 拼接字符串:使用append命令append "hello"$user",welcome!"

  • sleep:脚本进入睡眠,使用和其余语言同样,直接跟数字便可,单位为秒。

  • exit:退出

  • foreach:对指定集合的每个元素,依次赋值给变量。

    foreach [变量] {集合} {语句;} foreach i {1 2 3} { puts $i } 输出:1 2 3
  • exp_continue: 循环匹配,一般匹配以后会退出语句,但使用exp_continue 则能够不断循环执行某段语句。

    expect {
        "password" { send "$password\r" exp_continue # 不断匹配字符串"password",只要匹配成功就send } } expect eof
  • shell 嵌套使用expect,使用重定向,须要注意EOF之间的互相对应,而且变量须要在shell中定义,否者将会找不到变量,expect引用变量部分将是空内容,如同变量消失。若是想使在expect里定义的变量生效,使用<<\EOF...EOF,或用引号将第一个EOF引发来,即<<"EOF"...EOF,这样expect中set定义的变量,遍历时赋值的变量以及expect数组就都能使用了,可是相对的,shell里定义的变量也就不能使用了。

    #!/bin/bash hostname=$1 #接收第一个参数 password=$2 /usr/bin/expect <<-EOF # 重定向到expect,想使用expect中set定义的变量,须要转义\$ spawn ssh root@${hostname} # 或使用\EOF,但若是是\EOF,将不能使用Shell的变量 expect { "(yes/no)" { send "yes\r" expect "*assword:" {send "$password\r"} } "password" { send "$password\r" } } expect eof EOF # 因为用的-EOF,这里的EOF能够有空格,tab键 /usr/bin/expect <<EOF set m_pm(1) "hello" set m_pm(2) "world" puts "\$m_pm(2)" foreach i {1 2 3} { puts \$i } expect eof EOF
  • excpet中执行shell语句,exec sh -c {shell语句},多用于赋值变量,须要注意的是,expect里使用exec执行的shell语句,即便有打印和交互内容(echo,read命令)也不会输出到终端,即执行了命令,你并不知道是否出错,也不知道执行结果,若是须要将shell中echo命令打印的内容输出到终端,只能将执行结果赋值给expect变量,再使用puts命令打印出来,但即便这样,也会出现一些莫名头疼的问题,因此尽可能不要在expect中调用复杂的shell语句。你也可使用匹配字符,send “命令\r” 的方式执行shell命令,至关于交互互动,如expect ":~#" { send "ls\r" } 匹配到root登陆后的终端待输出状态,send发送ls命令并回车。

    exec sh -c {shell 命令} # 执行的shell命令即便有打印和须要交互的内容也不会出如今终端 set test_echo [exec sh -c {echo "test"}] puts "$test_echo"
  • expect/shell互相使用彼此变量

    • 若是二者在同一文件中,二者只是做为一段语句存在,使用#!/bin/bash解释的shell文件,expect调用shell变量直接$变量,和shell脚本调用变量方式并没有异同,使用#!/usr/bin/expect解释的expect脚本文件,shell做为expect文件的语句,如set a [exec sh -c {echo \$LAB}]调用expect变量,须要在expect里面设置环境变量。

      如:set ::env(LAB) my_lab

    • 若是二者是分别为不一样文件,expect做为脚本在shell脚本文件中被调用,如./test.excp,首先须要在shell中进行变量export, 例如export a="test", 而后在expect脚本文件中经过 $::env(a) 引用shell脚本文件的变量,例如set a_exp \$::env(a),同时也能够经过执行子shell调用,例如: set a [exec sh -c {echo $a}]
      在这里插入图片描述
      在这里插入图片描述
  • 向进程发送Ctcl + c,若是想向远端发送Ctrl-C结束远端进程,能够经过send "\003" 实现。

     

参考博客:

果冻想-Linux expect详解

taoyuanforrest-expect使用技巧

能力有限,不免出错,请多交流指出!!!
相关文章
相关标签/搜索