背景
在Linux进行ssh多次登录,sudo输入切换用户,打开screen/tmux时需要多次输入重复命令,思考如何解决重复性输入问题,经过探索找到了Expect脚本,其主要是用TCL语法(在网络仿真模拟中有用到过)。本文主要真对上述场景遇到的问题和基本语法做介绍,另外读者可以通过给出的资料文档进一步探索。
简介
维基百科: Expect
SourceForge: The Expect Home Page
TCL语法
由于Expect是建立在TCL语言基础上的一个工具,首先看一些TCL常见语法。
变量
一般用于保存hostname/username/password
1 2 3 4 5 6 7 8 9
| set username "leo" set hostname "domain.local"
set hostname [lindex $argv 0]
set passwrod [lindex $argv 1]
|
数组
一般用于保存服务器列表,显示并供用户选择
1 2 3 4 5 6 7
| set host_list(0) {host1 127.0.0.1 root} set host_list(1) {host2 192.168.1.1 admin} set host_list(2) {host3 8.8.8.8 dns}
set arrlen [array size host_list]
|
获取用户输入
一般用于接收用户输入选择,如密码、服务器等
1 2 3 4 5 6 7 8 9 10
| system stty -echo
send_user "please input password:"
expect_user -re "(.*)\n"
set password $expect_out(1, string)
system stty echo
|
if条件分支
用于逻辑判断
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| [string compare $host "123"]
set host [lindex $argv 0] if ![string compare $host ""] { }
expect_user -re "(.*)\n" set choose $expect_out(1,string) if {[string compare [string toupper $choose] "N"] == 0} { exit } elseif {[scan $choose {%[0-9]} choose] == 0} { exit } elseif { $choose < 0 || $choose >= $len } { exit }
|
for循环
1 2 3 4 5
| set len [array size host_list] for {set index 0} {$index < $len} {incr index} { puts "$index -> $host_list($index)" }
|
Expect应用介绍
接受窗口改变信号
如果是用expect登录后,是用screen或tmux,在用户终端大小调整情况下,因为窗口大小改变信号为同步到远程服务器会导致内容错乱。
1 2 3 4 5
| trap { set rows [stty rows] set cols [stty columns] stty rows $rows columns $cols < $spawn_out(slave,name) } WINCH
|
ssh自动登录
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35
| set timeout 10
spawn ssh $username@$hostname
expect { "yes/no" { send "yes\r"; exp_continue } "passsword" { send "$password\r"; exp_continue } "$" { send "hostname\r" } "Permission denied" { send_user "Permission denied (publickey,password)."; exit } incorrent { puts "Invalid account or password!"; exit } timeout { puts "Connection to $hostname timeout!"; exit } eof { puts "Connection to $hostname failed: $expect_out(buffer)"; exit } }
expect { "\\\$" { send "whoami\r" } }
sleep 0.2 send "script /dev/null\r" expect "Script started" sleep 0.2 send "screen -ls\r" expect { -re ".*Detached" { send "screen -r $username\r" } -re ".*Attached" { send "screen -x $username\r" } -re "/var/run/screen" { send "screen -dmS $username; screen -r $username\r" } }
interact
|
exp_continue可以理解为for/while的continue,Expect可以看作重新执行expect匹配