我們經(jīng)常會遇到一些需要與服務(wù)器程序打交道的場景,比如涛舍,從登陸某個(gè)服務(wù)器澄惊,然后進(jìn)行某項(xiàng)工作。這很平常富雅,但是如果把這個(gè)工作自動化進(jìn)行掸驱,你就需要一個(gè)程序能自動做你要告訴機(jī)器的事情,這樣吹榴,我們的expect就能大顯身手了亭敢。
首先,expect是一個(gè)簡單的工具語言图筹,如要工作就是進(jìn)行自動化的人機(jī)交互帅刀。它的作者對Expect的定義:是一個(gè)實(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"
exit
EOF
現(xiàn)在來解釋一下:
expect 的核心功能扣溺,對于設(shè)定好的特定匹配形式,以相匹配的動作以應(yīng)對瓜晤。每一個(gè)expect后所跟的字符串(或者正則表達(dá)式)就是腳本所等待的匹配模式锥余,每一個(gè)send 所做的工作就是對于各種的模式串,實(shí)施相應(yīng)的動作痢掠。
第一行設(shè)定了腳本執(zhí)行的程序,-f選項(xiàng)指的是expect執(zhí)行一個(gè)文件
第二行驱犹,設(shè)定了本腳本所有的超時(shí)時(shí)間,單位是秒(s),如果超時(shí)足画,腳本將繼續(xù)向下進(jìn)行(比如在等待某個(gè)模式出現(xiàn)雄驹,超時(shí)以后,會進(jìn)行下一語句淹辞,這里的下一句是expcet匹配超時(shí)失敗医舆,但依然執(zhí)行的是下一行send,或許將send用{}括起來可以解決這個(gè)問題)。
第三行蔬将,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ī)器(這個(gè)send "exit\r"前也可以有上一個(gè)命令的輸出,也可以沒有力喷,因?yàn)樯弦粋€(gè)命令執(zhí)行完畢后會順序執(zhí)行下一條)刽漂。 expect {}是多行期望,匹配到哪條執(zhí)行哪條弟孟。背景:有時(shí)執(zhí)行shell后預(yù)期結(jié)果是不固定的贝咙,有可能是詢問是yes/no,有可能是去輸入密碼拂募,所以可以用expect{}庭猩。花括號內(nèi)放多行語句陈症,從上至下匹配蔼水,匹配到哪個(gè)expect執(zhí)行哪句。
最后是等待標(biāo)示子進(jìn)程已結(jié)束的標(biāo)示符eof录肯,然后退出趴腋。(注:這個(gè)等待eof必須要有,如果沒有eof论咏,很可能在子進(jìn)程沒有結(jié)束前就退出优炬,造成問題。)
interact:退出expect返回終端厅贪,可以繼續(xù)輸入蠢护,否則將一直在expect不能退出到終端
上面腳本執(zhí)行效果:
[圖片]
模式例子:
expect {
"A" { do a }
"B" { do b }
timeout { do timeout }
}
過程
某些代碼有時(shí)是需要重復(fù)操作的,比如手機(jī)在某些特定的情況下可能需要反復(fù)重啟等卦溢,此時(shí)我們可以將其寫在某一個(gè)過程中糊余,直接調(diào)用該過程,以減少和簡化代碼单寂。
proc restartPhone {x} {
if {$x == 1} {
spawn adb shell
expect "~# "
send "reboot\r"
expect "*"
} else {
}
}
如上贬芥,在需求重啟手機(jī)時(shí),我們只要調(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"
exit
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"
exit
EOF
echo "logouted from $IP" >> $IPLOG_FILE
done < $filename