一、概述
我們通過Shell可以實現簡單的控制流功能,如:循環、判斷等。但是對於需要交互的場合則必須通過人工來干預,有時候我們可能會需要實現和交互程序如telnet服務器等進行交互的功能。而Expect就使用來實現這種功能的工具。
Expect是一個免費的編程工具語言,用來實現自動和交互式任務進行通信,而無需人的干預。Expect的作者Don Libes在1990年開始編寫Expect時對Expect做有如下定義:Expect是一個用來實現自動交互功能的軟件套件 (Expect [is a] software suite for automating interactive tools)。使用它系統管理員 的可以創建腳本用來實現對命令或程序提供輸入,而這些命令和程序是期望從終端(terminal)得到輸入,一般來說這些輸入都需要手工輸入進行的。 Expect則可以根據程序的提示模擬標准輸入提供給程序需要的輸入來實現交互程序執行。甚至可以實現實現簡單的BBS聊天機器人。:)
Expect是不斷發展的,隨着時間的流逝,其功能越來越強大,已經成為系統管理員的的一個強大助手。Expect需要Tcl編程語言的支持,要在系統上運行Expect必須首先安裝Tcl。
二、Expect工作原理
從最簡單的層次來說,Expect的工作方式象一個通用化的Chat腳本工具。Chat腳本最早用於UUCP網絡內,以用來實現計算機之間需要建立連接時進行特定的登錄會話的自動化。Chat腳本由一系列expect-send對組成:expect等待輸出中輸出特定的字符,通常是一個提示符,然后發送特定的響應。
expect用法
#!/usr/bin/expect
set timeout 30
spawn ssh -l username 192.168.1.1
expect "password:"
send "ispass\r"
interact
1. [#!/usr/bin/expect]
這一行告訴操作系統腳本里的代碼使用那一個shell來執行。這里的expect其實和linux下的bash、windows下的cmd是一類東西。
注意:這一行需要在腳本的第一行。
2. [set timeout 30]
“set 自定義變量名”:設置超時時間的,現在你只要記住他的計時單位是:秒 。timeout -1 為永不超時
3. [spawn ssh -l username 192.168.1.1]
spawn是進入expect環境后才可以執行的expect內部命令,如果沒有裝expect或者直接在默認的SHELL下執行是找不到spawn命令的。所以不要用 “which spawn“之類的命令去找spawn命令。
它主要的功能是給ssh運行進程加個殼,用來傳遞交互指令。可以理解為啟動一個新進程
4. [expect "password:"]
從進程接收字符串,這里的expect是expect的一個內部命令,expect的shell命令和內部命令是一樣的,但不是一個功能。這個命令的意思是判斷上次輸出結果里是否包含“password:”的字符串,如果有則立即返回,否則就等待一段時間后返回,這里等待時長就是前面設置的30秒;
5. [send "ispass\r"]
send接收一個字符串參數,並將該參數發送到進程。這里就是執行交互動作,與手工輸入密碼的動作等效。 命令字符串結尾別忘記加上“\r”,表示“回車鍵”。
6. [interact]
允許用戶交互;執行完成后保持交互狀態,把控制權交給控制台,這個時候就可以手工操作了。如果沒有這一句登錄完成后會退出,而不是留在遠程終端上。
7.$argv 參數數組
expect腳本可以接受從bash傳遞過來的參數.可以使用[lindex $argv n]獲得,n從0開始,分別表示第一個,第二個,第三個....參數
7. 下面的expect腳本的例子
執行這個文件./launch.exp 1 2 3
屏幕上就會分別打印出參數
send_user用來發送內容給用戶。
參數運用方面還有很多技巧
比如$argc 存儲了參數個數,args被結構化成一個列表存在argv。$argv0 被初始化為腳本名字。
除此之外,如果你在第一行(#!那行)使用-d (debug參數),可以在運行的時候輸出一些很有用的信息
比如你會看見
argv[0] = /usr/bin/expect argv[1] = -d argv[2] = ./launch.exp argv[3] = 1 argv[4] = 2 argv[5] = 3
使用這些也可以完成參數傳遞
8.
expect的命令行參數參考了c語言的,與bash shell有點不一樣。其中,$argc為命令行參數的個數,$argv0為腳本名字本身,$argv為命令行參數。[lrange $argv 0 0]表示第1個參數,[lrange $argv 0 4]為第一個到第五個參數。與c語言不一樣的地方在於,$argv不包含腳本名字本身。
9.
exp_continue的用法
#!/usr/bin/expect -f
set ipaddr "localhost"
set passwd "iforgot"
spawn ssh root@$ipaddr #spawn 意思是執行命令,expect內命令,shell中不存在
expect {
"yes/no" { send "yes\r"; exp_continue}
"password:" { send "$passwd\r" }
}
expect "]# "
send "touch a.txt\r" #意思為發送命令
send "exit\r"
expect eof
exit
幾個例子:
1、
expect最常用的語法是來自tcl語言的模式-動作。這種語法極其靈活,下面我們就各種語法分別說明。
單一分支模式語法:
expect "hi" {send "You said hi"}
匹配到hi后,會輸出"you said hi"
多分支模式語法:
expect "hi" { send "You said hi\n" } \ "hello" { send "Hello yourself\n" } \ "bye" { send "dat was unexpected\n" }
匹配到hi,hello,bye任意一個字符串時,執行相應的輸出。等同於如下寫法:
expect { "hi" { send "You said hi\n"} "hello" { send "Hello yourself\n"} "bye" { send "That was unexpected\n"} }
2、
set timeout -1 spawn ftp ftp.test.com //打開新的進程,該進程用戶連接遠程ftp服務器 expect "Name" //進程返回Name時 send "user\r" //向進程輸入anonymous\r expect "Password:" //進程返回Password:時 send "123456\r" //向進程輸入don@libes.com\r expect "ftp> " //進程返回ftp>時 send "binary\r" //向進程輸入binary\r expect "ftp> " //進程返回ftp>時 send "get test.tar.gz\r" //向進程輸入get test.tar.gz\r
下載完ftp文件時,仍然可以停留在ftp命令行狀態,以便手動的執行后續命令。interact可以達到這些目的。下面的demo在自動登錄ftp后,允許用戶交互。
spawn ftp ftp.test.com expect "Name" send "user\r" expect "Password:" send "123456\r" interact
4、
下面一段腳本實現了從機器A登錄到機器B,然后執行機器B上的pwd命令,並停留在B機器上,等待用戶交互。
#!/home/tools/bin/64/expect -f set timeout -1 spawn ssh $BUser@$BHost expect "*password:" { send "$password\r" } expect "$*" { send "pwd\r" } interact