利用Expect編寫交互式腳本更改遠(yuǎn)程服務(wù)器密碼

我們經(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
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容