shell學習之四---expect命令
expect可以實現shell實現不了的使用者互動的需求。expect可以將互動寫在一個指令碼上,完成很多自動化的動作,比如ssh、ftp登陸等,都是需要互動需求的。expect是需要安裝的,直接yum -y install expect安裝即可。
expect的四個關鍵命令為:spawn、expect、send、set,其中spawn是呼叫要執行的命令,expect是等待命令提示資訊的出現也即捕捉使用者提示資訊,send是傳送需要互動的資訊來替代手工的輸入,set則是輸入變數值。
一、expect關鍵語法
1、關鍵語法
[#!/usr/bin/expect] 這一行告訴作業系統腳本里的程式碼使用那一個shell來執行。這裡的expect其實和linux下的bash、windows下的cmd是一類東西。 注意:這一行需要在指令碼的第一行。
spawn 呼叫要執行的命令
expect 只有spawn執行的命令的執行結果才被expect捕捉到,主要包括標準輸入的提示資訊、eof和timeout。等待命令提示資訊的出現,也就是捕捉使用者輸入的提示:
send 傳送需要互動的值,替代了使用者手動輸入內容
set 設定變數值
interact 執行完成後保持互動狀態,把控制權交給控制檯,這個時候就可以手工操作了。如果沒有這一句登入完成後會退出,而不是留在遠端終端上。
expect eof 這個一定要加,與spawn對應表示捕獲終端輸出資訊終止,類似於if....endif
PS:expect指令碼必須以interact或expect eof結束,執行自動化任務通常expect eof就夠了。
2、常見設定
set timeout -1 設定expect永不超時
set timeout 300 設定expect 300秒超時,如果超過300沒有expect內容出現,則退出
PS:為什麼要設定超時時間,因為預設時間是10s,則在互動執行的過程中,很可能會斷開,導致任務執行沒完成就終止。我司的一個從遠端ftp上同步檔案的計劃任務總是執行終止,後來設定了超時時間為-1就可以了。
二、expect的用法示例
1、一個簡單的拉取檔案的例子
#!/usr/bin/expect -f
set passwd "123456" ##這個是你設定的密碼
set timeout -1 ##設定超時時間
spawn sftp -P 2022 [email protected]
expect "password:"
send "$passwd\r"
expect "sftp>"
send "get -r data/201711*\r"
expect "sftp>"
send "exit\r"
interact
2、一個小例子,用於linux下賬戶的建立:1 #!/usr/bin/expect
2
3 set passwd "mypasswd"
4 set timeout 60
5
6 if {$argc != 1} {
7 send "usage ./account.sh \$newaccount\n"
8 exit
9 }
10
11 set user [lindex $argv [expr $argc-1]]
12
13 spawn sudo useradd -s /bin/bash -g mygroup -m $user
14
15 expect {
16 "assword" {
17 send_user "sudo now\n"
18 send "$passwd\n"
19 exp_continue
20 }
21 eof
22 {
23 send_user "eof\n"
24 }
25 }
26
27 spawn sudo passwd $user
28 expect {
29 "assword" {
30 send "$passwd\n"
31 exp_continue
32 }
33 eof
34 {
35 send_user "eof"
36 }
37 }
38
39 spawn sudo smbpasswd -a $user
40 expect {
41 "assword" {
42 send "$passwd\n"
43 exp_continue
44 }
45 eof
46 {
47 send_user "eof"
48 }
49 }
以上指令碼的注意點如下:第3行: 對變數賦值的方法;
第4行: 預設情況下,timeout是10秒;
第6行: 引數的數目可以用$argc得到;
第11行:引數存在$argv當中,比如取第一個引數就是[lindex $argv 0];並且如果需要計算的話必須用expr,如計算2-1,則必須用[expr 2-1];
第13行:用spawn來執行一條shell命令,shell命令根據具體情況可自行調整;有文章說sudo要加-S,經過實際測試,無需加-S亦可;
第15行:一般情況下,如果連續做兩個expect,那麼實際上是序列執行的,用例子中的結構則是並行執行的,主要是看匹配到了哪一個;在這個例子中,如果你寫成序列的話,即
expect "assword"
send "$passwd\n"
expect eof
send_user "eof"
那麼第一次將會正確執行,因為第一次sudo時需要密碼;但是第二次執行時由於密碼已經輸過(預設情況下sudo密碼再次輸入時間為5分鐘),則不會提示使用者去輸入,所以第一個expect將無法匹配到assword,而且必須注意的是如果是spawn命令出現互動式提問的但是expect匹配不上的話,那麼程式會按照timeout的設定進行等待;可是如果spawn直接發出了eof也就是本例的情況,那麼expect "assword"將不會等待,而直接去執行expect eof。
這時就會報expect: spawn id exp6 not open,因為沒有spawn在執行,後面的expect指令碼也將會因為這個原因而不再執行;所以對於類似sudo這種命令分支不定的情況,最好是使用並行的方式進行處理;
第17行:僅僅是一個使用者提示而已,可以刪除;
第18行:向spawn程序傳送password;
第19行:使得spawn程序在匹配到一個後再去匹配接下來的互動提示;
第21行:eof是必須去匹配的,在spawn程序結束後會向expect傳送eof;如果不去匹配,有時也能執行,比如sleep多少秒後再去spawn下一個命令,但是不要依賴這種行為,很有可能今天還可以,明天就不能用了;
3、下面這個例子比較特殊,在整個過程中就不能expect eof了
1 #!/usr/bin/expect 2 set mypassword=123456 3 set timeout 30 4 spawn ssh 10.192.224.224 5 expect "password:" 6 send "mypassword\n" 7 expect "*$" 8 send "mkdir tmpdir\n" 9 expect "*$"這個例子實際上是通過ssh去登入遠端機器,並且在遠端機器上創佳一個目錄,我們看到在我們輸入密碼後並沒有去expect eof,這是因為ssh這個spawn並沒有結束,而且手動操作時ssh實際上也不會自己結束除非你exit;所以你只能expect bash的提示符,當然也可以是機器名等,這樣才可以在遠端建立一個目錄。注意,請不要用spawn mkdir tmpdir,這樣會使得上一個spawn即ssh結束,那麼你的tmpdir將在本機建立。當然實際情況下可能會要你確認ssh key,可以通過並行的expect進行處理,不多贅述。
PS:在shell指令碼中插入expect命令,格式為如下
#!/bin/sh expect <<! ##開頭 spawn ssh [email protected] expect "*Password*" send "xxx\r" expect ">" send "mkdir xxxxx\r" send "exit\r" expect eof ! ##結尾