1. 程式人生 > >shell學習之四---expect命令

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
!          ##結尾