我們經(jīng)常會遇到一些需要與服務(wù)器程序打交道的場景,比如,從登陸某個服務(wù)器,然后進(jìn)行某項(xiàng)工作。這很平常,但是如果把這個工作自動化進(jìn)行,你就需要一個程序能自動做你要告訴機(jī)器的事情,這樣,我們的expect就能大顯身手了。
首先,expect是一個簡單的工具語言,如要工作就是進(jìn)行自動化的人機(jī)交互。它的作者對Expect的定義:是一個實(shí)現(xiàn)自動交互功能的軟件套件(a software suite for automating interactive tools),使用expect,它能幫助我們在合適的情景下進(jìn)行合適的交互。
#! /usr/bin/expect -f
set timeout 20
spawn ssh root@$IP
expect {
"*RSA key*" {send "yes\r"}
"*password:" {send "$ORI_PASSWD\r"}
}
expect "*#*"
send "ls\r"
send "passwd\r"
expect {
"*password:" {send "$NEW_PASSWD\r"}
"新的 密碼:" {send "$NEW_PASSWD\r"}
}
send "exit\r"
expect eof
EOF
現(xiàn)在來解釋一下:
expect 的核心功能,對于設(shè)定好的特定匹配形式,以相匹配的動作以應(yīng)對。每一個expect后所跟的字符串(或者正則表達(dá)式)就是腳本所等待的匹配模式,每一個send 所做的工作就是對于各種的模式串,實(shí)施相應(yīng)的動作。
第一行設(shè)定了腳本執(zhí)行的程序,-f選項(xiàng)指的是expect執(zhí)行一個文件
第二行,設(shè)定了本腳本所有的超時時間,單位是秒(s),如果超時,腳本將繼續(xù)向下進(jìn)行(比如在等待某個模式出現(xiàn),超時以后,會進(jìn)行下一語句,這里的下一句是expcet匹配超時失敗,但依然執(zhí)行的是下一行send,或許將send用{}括起來可以解決這個問題)。
第三行,expect使用spawn(繁衍)命令來啟動腳本和命令會話,這里啟動的是ssh命令,這里的ssh命令將會以子進(jìn)程的方式產(chǎn)生。
下面就是交互的過程:ssh 登陸以后,會給要求客戶寫入密碼,所以等待出現(xiàn)“password:”,出現(xiàn)password:以后,需要寫入密碼,注意這里需要送去回車或者換行符,否則遠(yuǎn)端主機(jī)不會收到ssh請求的。登陸上系統(tǒng)之后,會出現(xiàn)命令提示符:~$,即系統(tǒng)已經(jīng)登陸到了遠(yuǎn)端主機(jī)的shell,然后送去要執(zhí)行的命令。完畢后推出遠(yuǎn)程機(jī)器(這個send "exit\r"前也可以有上一個命令的輸出,也可以沒有,因?yàn)樯弦粋€命令執(zhí)行完畢后會順序執(zhí)行下一條)。 expect {}是多行期望,匹配到哪條執(zhí)行哪條。背景:有時執(zhí)行shell后預(yù)期結(jié)果是不固定的,有可能是詢問是yes/no,有可能是去輸入密碼,所以可以用expect{}?;ɡㄌ杻?nèi)放多行語句,從上至下匹配,匹配到哪個expect執(zhí)行哪句。
最后是等待標(biāo)示子進(jìn)程已結(jié)束的標(biāo)示符eof,然后退出。(注:這個等待eof必須要有,如果沒有eof,很可能在子進(jìn)程沒有結(jié)束前就退出,造成問題。)
interact:退出expect返回終端,可以繼續(xù)輸入,否則將一直在expect不能退出到終端
上面腳本執(zhí)行效果:
[圖片]
模式例子:
expect {
"A" { do a }
"B" { do b }
timeout { do timeout }
}
過程
某些代碼有時是需要重復(fù)操作的,比如手機(jī)在某些特定的情況下可能需要反復(fù)重啟等,此時我們可以將其寫在某一個過程中,直接調(diào)用該過程,以減少和簡化代碼。
proc restartPhone {x} {
if {$x == 1} {
spawn adb shell
expect "~# "
send "reboot\r"
expect "*"
} else {
}
}
如上,在需求重啟手機(jī)時,我們只要調(diào)用”restartPhone 1"便可以了。
第一版批量修改機(jī)器登錄密碼腳本:
#! /bin/sh
master_ip=192.168.1.106
master_user=liuhao
master_passwd=toor
PWD=`pwd`
LOG_DIR=logd
filename=iptab
if [ ! -d $PWD/$LOG_DIR ];then
mkdir $PWD/$LOG_DIR
else
rm -rf $PWD/$LOG_DIR/*
fi
IPLOG_FILE=$PWD/$LOG_DIR/logIP
echo "" > $IPLOG_FILE
while read -r line
do
#echo $line
##下面一行代碼展示了如何將awk中的變量導(dǎo)出到shell(腳本)變量中使用的技巧
eval $(echo $line | awk '{printf("IP=%s;ORI_PASSWD=%s;NEW_PASSWD=%s",$1,$2,$3);}')
#echo $IP $ORI_PASSWD $NEW_PASSWD
echo "will login $IP" >> $IPLOG_FILE
expect <<-EOF
set timeout 40
spawn ssh root@$IP
expect {
"*RSA key*" {send "yes\r";exp_continue}
"*password:" {send "$ORI_PASSWD\r"}
timeout {send "touch $PWD/$LOG_DIR/$IP.loginfail\r";exit}
}
send "ifconfig\r"
expect {
"*$IP*" {send "ls\r"}
timeout {send "touch $PWD/$LOG_DIR/$IP.loginfail\r";exit}
}
send "passwd\r"
expect {
"*password:" {send "$NEW_PASSWD\r"}
"*新的*" {send "$NEW_PASSWD\r"}
timeout {send "exit\r";exit}
}
expect {
"*password:" {send "$NEW_PASSWD\r"}
"*重新*" {send "$NEW_PASSWD\r"}
timeout {send "exit\r";exit}
}
expect {
"*成功*" {send "touch $IP\r"}
"*updated successfully" {send "touch $IP\r"}
timeout {send "exit\r";exit}
}
send "ls\r"
expect {
"$IP" {
send "scp $IP $master_user@$master_ip:$PWD/$LOG_DIR/\r"
expect {
"*RSA key*" {send "yes\r";exp_continue}
"*password:" {send "$master_passwd\r"}
}
}
}
send "exit\r"
expect eof
EOF
echo "logouted from $IP" >> $IPLOG_FILE
done < $filename
第二版將日志信息直接寫入本地文件
#! /bin/sh
PWD=`pwd`
LOG_DIR=logd
filename=iptab
if [ ! -d $PWD/$LOG_DIR ];then
mkdir $PWD/$LOG_DIR
else
rm -rf $PWD/$LOG_DIR/*
fi
IPLOG_FILE=$PWD/$LOG_DIR/logIP
LOG_CHANGE=$PWD/$LOG_DIR/logChange
echo "" > $IPLOG_FILE
echo "" > $LOG_CHANGE
while read -r line
do
#echo $line
##下面一行代碼展示了如何將awk中的變量導(dǎo)出到shell(腳本)變量中使用的技巧
eval $(echo $line | awk '{printf("IP=%s;ORI_PASSWD=%s;NEW_PASSWD=%s",$1,$2,$3);}')
#echo $IP $ORI_PASSWD $NEW_PASSWD
echo "will login $IP" >> $IPLOG_FILE
expect <<-EOF
set timeout 40
#這里打開文件要用append追加模式,而不可以用w寫模式,寫模式默認(rèn)先清除文件內(nèi)容
set ofile [open "$LOG_CHANGE" a]
log_user 1
spawn ssh root@$IP
expect {
"*password:" {send "$ORI_PASSWD\r"}
"*RSA key*" {send "yes\r";exp_continue}
timeout {puts \$ofile "login $IP failed,login timeout";close \$ofile;exit}
}
expect {
"Permission denied*" {puts \$ofile "login $IP failed,wrong passwd";close \$ofile;exit}
"*#*" {send "ifconfig\r"}
timeout {puts \$ofile "login $IP failed";close \$ofile;exit}
}
expect {
"*$IP*" {send "ls\r"}
timeout {puts \$ofile "login $IP failed";close \$ofile;exit}
}
puts \$ofile "login $IP OK"
send "passwd\r"
expect {
"*password:" {send "$NEW_PASSWD\r"}
"*新的*" {send "$NEW_PASSWD\r"}
timeout {puts \$ofile "passwd $IP failed";close \$ofile;send "exit\r";exit}
}
expect {
"*password:" {send "$NEW_PASSWD\r"}
"*重新*" {send "$NEW_PASSWD\r"}
timeout {puts \$ofile "passwd $IP failed";close \$ofile;send "exit\r";exit}
}
expect {
"*成功*" {puts \$ofile "passwd $IP successfully"}
"*updated successfully" {puts \$ofile "passwd $IP successfully"}
timeout {puts \$ofile "passwd $IP failed"}
}
close \$ofile
send "exit\r"
expect eof
EOF
echo "logouted from $IP" >> $IPLOG_FILE
done < $filename