歡迎來到Linux教程網
Linux教程網
Linux教程網
Linux教程網
Linux教程網 >> Linux基礎 >> Linux教程 >> expect入門教程

expect入門教程

日期:2017/2/27 15:57:26   编辑:Linux教程
expect 能與交互式程序進行“可程序化”會話的腳本語言
expect [ -dDinN ] [ -c cmds ] [ -[f|b] ] cmdfile ] [ args ]  
概述:
expect是一種能夠按照腳本內容裡面設定的方式與交互式程序進行“會話”的程序。根據腳本內容,expect可以知道程序會提示或反饋什麼內容以及什麼是正確的應答。它是一種可以提供“分支和嵌套結構”來引導程序流程的解釋型腳本語言。另外,還可以在隨時需要的時候把控制權交給用戶,然後再還給腳本。

expectK是expect和Tk的混合體。它就按照expect和Tk的方式運行。expect也可以直接嵌入到C或是C++程序中(這種情況是不涉及tcl解釋)。請看libexpect(3).

expect的名字是從被UUCP(UNIX到UNIX的拷貝),Kermit(一種文件傳輸協議, 由哥倫比亞大學設計)和一些其他Modem控制等程序設計思維大眾化的“send/expect”時序理念中得出的。不像UUCP,expect已經被廣泛應用於很多你可以想像的到的程序和任務當中了。

expect還能同時和多個程序交互。

例如:下面是一些expect可以做到的事情
讓你的計算機呼叫你,這樣你可以不用付呼叫費。
啟動一個游戲(例如:rogue),如果不是最佳配置,則一直重新啟動,直到是最佳配置為止。然後把控制權轉交給你。

運行fsck的時候,用”yes”或是”no”來回應fsck的交互問題。在沒有預設答案標准的情況下把控制權返還給你。

連接到另一個網絡或是BBS站點,自動收取你的郵件,就像郵件是發往你的當地系統一樣。

在執行rlogin,telnet,tip,su,chgrp等等命令的時候保存“環境變量”,“當前目錄”或是其他一些信息。

有很多原因致使shell不能完成這樣的任務(你自己可以試試看)。而這一切對於expect來說都是可以的。一般情況下,當一個程序需要程序與用戶進行交互的時候就需要用到expect。還需要的一個前提是這種交互必須能程序化(例如:循環結構,選擇結構等等,個人認為必須是有規律可循的)。如果需要的話,expect還能把控制權返還給用戶。同樣,用戶也可以在任何時候把控制權還給腳本程序。

用法:
expect通過讀取cmdfile(命令文件)來執行一系列指令。只要系統支持“#!“,在script腳本文件的首行標明“#!/usr/local/bin/expect –f“,並賦予腳本文件可執行權限,執行腳本文件就可以(隱含方式或是默認)調用expect。當然,上面的路徑必須正確地指明expect解釋程序的位置。/usr/local/bin只是一個例子。

-c 選項用來標明需要在執行腳本內容之前來執行的命令。
這條命令(-c選項後的命令)應該用引號括起來,以免在執行時被shell分開解釋。
-c選項可能會被反復使用。多條命令可以使用同一個”-c”,命令之間需要用分號隔開。這些命令會按照它們出現的先後順序執行。(在expectk中,”-c”相當於”-command”)。

-d 選項允許輸出調試性信息。這些信息主要報告像expect和interact等命令執行時的內部行為。這個選項與寫在腳本開頭的”exp_internal 1”具有同樣的效果,同時還會打印出expect的版本。(strace命令用在跟蹤變量聲明,trace命令用於跟蹤變量的賦值)(在expectk中,”-d”相當於”-diag”)

-D 選項開啟交互調試器。後面必須跟有一個整數值作為參數,當值為非零或是按下CTRL+C的時候(或是遇到斷點,或是在腳本中恰好出現其他的調試語句),調試器會在進行下一次Tcl Procedure前取得控制權。想了解更多信息請參見README文件或是下面的SEE ALSO。(在expectk中,這個選項相當於”-Debug”)。

-f 選項指明從哪個文件中讀取命令。這個選項是可選的,因為只有當使用”#!”時它才有可能被用到。而其他選項可以寫在命令行中。(在expectk中,它相當於”-file”)。
默認情況下,命令文件是全部讀入內存一並執行的。但有些時候需要每次只讀一行。例如:stdin(標准輸入)就是這樣讀取的。如果強制任意文件以這種方式(每次讀一行)執行的話就使用”-b”選項。(在expectk中,它相當於”-buffer”)。
如果”-“被一個文件名替代,那麼腳本就會用讀指定文件的方式來替代從標准輸入讀的方式。(例如:”./ -“就表示從一個名為”-”的文件中讀所需的信息)。

-i 選項使expect能交互式的提示輸入命令,而不是從文件中讀取。在遇到文件尾或是執行了exit命令時,提示輸入命令終止。要了解更多信息請參見下面的interpreter。-i 選項是假設既不是從一個命令文件讀,也沒有使用-c選項。(在expectk中,它相當於”-interactive”)。

-- 是用來為劃定選項尾的。當你需要像使用選項一樣傳一個參數,但希望這個參數不要被當作選項解釋時,就需要用到這個選項。當阻止其他選項時,可以把它放在”#!”行中。例如:下面的例子會讓所有參數(包括腳本文件名)都存儲在argv中。
#!/usr/local/bin/expect --
說明 :上面最後是2個橫槓
注意:當在”#!”行中使用參數時,必須遵守getopt(3)和execve(2)的規定。

$exp_library下如果有expect.rc這個文件的話,它會自動被加載為資源文件(應該是類似於標准配置文件,像用戶根目錄下的.bash_profile文件一樣)。除非使用-N選項取消自動加載。(在expectk中,它相當於”-NORC”)。這個文件被加載後,緊接著用戶根目錄下的.expect.rc(~/.expect.rc)會被加載。除使用-n選項取消。如果定義了環境變量DOTDIR,那麼它被認為是存放有.expect.rc文件的目錄。然後從這個目錄中讀取.expect.rc文件。(在expectk中,它相當於”-norc”)。這些加載配置文件的動作是出現在執行完-c選項指定的命令之後。

-v 選項用來打印出版本號,然後退出。(在expectk中的相應選項是-version)。
可選的參數匯成一列,存放在變量argv中。argc被初始化為argv的長度(變量個數)。argv0被設置為腳本名稱(or binary if no script is used)。例如:下面的例子打印出腳本的名稱和前三個參數。
send_user “$argv0 [lrang $argv 0 2 ]\n”

命令:
expect使用tcl語言(Tool Command Language).tcl提供諸如流控制,表達式值和一些其他的特性。像遞歸調用,定義函數等等。在這裡用到的沒有說明的命令都是Tcl命令。

expect支持一些額外的命令。下面具體描述。除非另外聲明,否則命令返回空字符串。命令按字母順序排序,這樣便於查找。僅管如此,初學者還是覺得按照”spawn , send , expect , interact”這種方式來讀比較容易。

注意,Exploring Expect這本書中提供了關於”expect和Tcl”的介紹。這本Manpage手冊中也提供了一些例子.但數量有限,因為這本是做為入門的教材手冊使用的.

在本手冊中,以E開頭的是指Expect程序,小寫e開頭的是指expect命令.
close [-slave] [-onexec 0|1] [-i spawn_id]
關閉與當前進程的連接。大多數交互程序會在它們的stdin(標准輸入)中檢測到EOF(文件尾),然後退出.所以通常close也有能力殺死進程.-i 選項指定了要殺死的對應於spawn_id的進程。expect和interact都能檢測到當前程序的退出,並隱含的執行一個關閉。如果你通過執行”exec kill $pid”來殺死進程的話,那麼你就需要再顯式的調用一下close.

-onexec 選項用來確定spawn_id在開始新的spawned process(我將其翻譯為監測進程)時是被關閉還是要被覆蓋。如果想保持這個spawn_id打開的話,那麼後面的參數需要設為0。一個非零值將會使spawn_id關閉,並可以將這個spawn_id用於新的進程(默認行為)。-slave選項是用來關閉從屬進程.(參見spawn -pty)。如果在連接中止的時候,從屬進程還打開的話,那麼它將自動關閉。不管進程是顯式的調用或是隱式的被中止,你都需要調用wait命令來清理進程執行的殘余。close不會調用wait。因為在關閉進程的時候,並不能保障它“正常退出”(個人認為,可能是指退出時做相應的清理工作)。要了解更多的信息,請參見wait命令。
debug [[-now] 0|1]

控制Tcl調試器以步進方式執行語句,設置斷點等等。在沒有參數的情況下,如果調試器沒有運行,返回1,否則返回0。用1做參數時,啟動調試器,用0做參數時,停止調試器。如果連同-now一起使用的話,調試器將立即啟動(也就是說,在debug命令當中)。否則調試器會在執行下一條語句的時候啟動。

調試命令不會改變任何的traps。可以參見以-D選項啟動Expect(參見上面)
要了解更多關於debugger的內容,請參見README文件和下面的SEE ALSO。
disconnect
從終端關閉與一個克隆進程的連接,但讓它在後台繼續運行。這個進程將被賦予為單獨的進程組(如果可能的話)。標准I/O被重定向到/dev/null。下面的代碼使用disconnect命令使腳本在後台繼續運行。
if [fork]!=0 exit
disconnect
下面的腳本需要讀取一個密碼,然後每小時執行一次,每次執行的時候都要求輸入密碼。腳本提供了所需的密碼,所以你只需輸入一次就可以了。(參見能關閉回顯的終端命令)。
send_user "password?\ "
expect_user -re "(.*)\n"
for {} 1 {} {
if [fork]!=0 {sleep 3600;continue}
disconnect
spawn priv_prog
expect Password:
send "$expect_out(1,string)\r"
. . .
exit  
}
用這種方式,而不是用shell後台方式來執行程序的好處是,用disconnect可以在關閉前保存終端參數,然後把它們應用於新的終端中。如果使用&的話,Expect沒有機會讀取終端參數,因為在Expect取得控制權的時候,終端已經退出了。
exit [-opts] [status]
使expect退出或是准備退出。-onexit選項使下一個參數做為退出的句柄被使用。沒有參數時,返回當前的退出句柄。-noexit選項使Expect准備退出,而不是把控制權暫時返還給系統。用戶定義的退出句柄和Expect內部的句柄都是以同樣的方式被返回。接下來將不再執行Expect命令。這在Tcl擴展環境下執行Expect時非常有用。保留當前的解釋器(在Tk環境中的主窗口)以清除其他的Tcl擴展。如果Expect再次調用exit(這有可能發生),不會返回句柄。退出時,全部連接將關閉,關閉的動作會被監測的進程檢測為“到文件尾”。exit只按照正常exit(2)的中的語句來執行,不會引發其他的動作。因此監視的進程如果沒有檢測到“到文件尾”的話,會繼續執行。(能檢測更多的情況是很重要的。例如:什麼樣的信號會發給監測進程,但這些是由系統決定的,它們放在exit(3)的文檔中)。如果被監測的進程繼續運行的話,將會被init繼承。當前的狀態信息將做為Expect的退出信息返回(如果沒有指明的話,返回信息為0)。退出命令在腳本程序執行的最後才自動隱含的調用。
exp_continue [-continue_timer]
這個命令可以使expect繼續執行而不是正常的返回.默認情況下,exp_continue會重高超時時鐘,-continue_timer選項會阻止時鐘重新計數(連續計數).
Exp_internal [-f file] value
如果是value非零的話,使接下來的命令將調試信息輸出到Expect和標准錯誤輸出.如果是0的話,輸出的信息將會被屏蔽.調試信息包括收到的每條信息和每次嘗試用當前輸出與腳本中的模式相匹配的信息.如果設置了輸出文件,那麼正常的和調試的信息都會被寫到這個文件當中.(忽略上面value選項的值).任何之前打開的調試輸出文件將會被關閉.-info選項使exp_internal返回最近關於non-info參數的描述.
exp_open [args] [-I spawn_id]
它返回對應於原始spawn id的文件描述符.這樣這個文件描述符就可以被使用了,就好像這個文件是被Tcl的open指令打開的一樣.(這個spawn id將不再使用,wait指令將不能用在這個進程.).-leaveopen選項使spawn id保持打開,以便供Expect命令使用.
exp_pid [-i spawn_id]
它將返回對應於當前被跟蹤進程的ID.如果使用-i選項,將返回對應於指定的spawn id的進程ID.
Exp_send  
它是send的別稱  
Exp_send_error  
它是Send_error的別稱  
Exp_send_log  
它是Send_log的別稱  
Exp_send_tty  
它是Send_tty的別稱  
Exp_send_user  
它是Send_user的別稱  
Exp_version [[-exit] version] 它用於確保腳本程序與當前的Expect兼容。
在沒有參數的情況下,返回當前Expect的版本.這個版本就會編譯到腳本中.如果你確切的知道你的腳本程序不需要最新版本的特性,可以指定一個以前的版本。

版本號由三個由句點分隔的數字組成.第一個是主序號.對應某一主序號版本的Expect寫的腳本程序,在不同主序號版本的Expect環境下基本不能正常運行.exp_version在主版本不同的情況下會返回一個錯誤.第二個數字是次版本號.編寫腳本的Expect的次版本號如果比當前的Expect大的話,可能會用到一些新的特性,在當前的環境下可能不能正常運行.exp_version會在當主序號相同,但次序號比當前Expect版本大的時候返回一個錯誤信息.第三個數字在Expect的版本比較中沒有多大作用.它只是當發行版有任何變化的時候會增加.比如說增加一些新的文檔或是做了優化.當升級到一個新的次版本號時,這個數字會被初始化為零.如果使用了-exit選項,Expect會在當前的版本過期的時候打印一個錯誤信息,然後退出.
expect [[-opts] pat1 body1] ... [-opts] patn [bodyn]
等待直到被監視進程的輸出與設定的模式相匹配,或是一個指定的時間過後,或是遇到文件尾.如果最後的body是空的,那麼它將被忽略.最近的expect_before設定的模式會在其他模式之前被隱含地使用.最近的expect_after設定的模式將在所有其他模式匹配完後才被調用.如果整個Expect命令的參數超過一行,這個參數可能被分為多行,各行之間用一個”\”連接,以防被分開解釋.在這種情況下,Tcl解釋器通常會置換掉”\”.如果一個模式設定為eof.則相應的語句被在當達到文件尾的時候執行.如果一個模式設定為timeout,那麼相應的語句會在超時時執行.如果沒有設定timeout對應的執行語句,將會在timeout時隱含執行空指令.即不執行任何語句.默認的超時時鐘設的是10秒,但可以自己設定.通過”set timeout 30”,可以將超時時鐘設定為30秒.如果設定為-1的話,那麼超時時鐘將是無窮大,如果一個模式設定為default,那麼相應的語句將會在遇到文件尾或是超時時執行.如果觸發了相應的模式,則此模式對應的語句將會被執行.Expect返回語句執行的結果(或是在沒有模式觸發的情況下是空字符串).在多種模式匹配的情況下,第一個匹配的模式對應的語句將被執行。

每次出現新的輸出的時候,它們都會依次匹配相應的模式.因此,如果你想測試匹配是否成功,可以把最後一個模式設定為肯定會出現的東西,例如一個提示符.在沒有提示符的情況下,你需要使用一個timeout模式。模式被定義為三種類型.默認情況下,模式被定義為Tcl的string match(字符串匹配)指令.(這些模式很像C Shell中的正則表達式,它們通常被用來做模糊匹配).-gl選項保護那些可能被認為是Expect的選項的模式匹配字符串。以”-”開頭的所有模式匹配字符串都需要這樣保護起來。(因為默認情況下,以”-”開頭的字符串都被保留起來做為將來的選項)。例如:下面的代碼期望一個正確的登錄.(注意下面的abort是一個已經在腳本的其他位置定義好的函數)
expect {  
busy {puts busy\n ; exp_continue}  
failed abort  
"invalid password" abort  
timeout abort  
connected  
}  
在第四行需要使用引號,因為它包含了一個空格.如果不用引號把模式括起來的話,它會被分別解釋為模式與執行函數.執行同樣動作的其他模式同樣需要把執行函數的名稱寫出來(像其他兩個執行”abort”函數的模式),如果使用regexp-style模式的話(看下面的例子),更多關於建立glob-style模式的信息請參見Tcl手冊.regexp模式以-re開頭.上面的例子可以用regexp模式改寫成下面的代碼:
expect {
busy {puts busy\n ; exp_continue}
-re "failed|invalid password" abort
timeout abort
connected
}
兩種類型都可以被正確匹配。這就是說設置的類型可以不用是整個字符串。可以只匹配頭部或是尾部(就假設其他部分也匹配一樣)。用^來匹配字符串頭部。用$來匹配字符串尾部。如果你不希望等待直到字符尾,你可以在監視進程回顯字符的中間時刻結束響應。雖然仍能打印出正確的結果,但最後的輸出可能看上去有點混亂。因此如果能夠詳細描述預期的字符串尾部的話,還是鼓勵使用$來匹配尾部。在許多編輯器中,^和&分別表示首行和尾行。因為Expect不是基於行緩沖的程序。所以這兩個字符分別用來表示當前匹配緩沖區中的頭數據和尾數據。-ex使模式進行精確匹配。這時,不對*,^等字符進行解釋(但還是要遵守Tcl的規則)。Expect patterns are always unanchored.

-nocase選項使輸出中的大寫字符也按小寫字符匹配。模式匹配字符串本身改變。在讀取輸出進行匹配時,超過2000字符將會強制將前面的字符丟棄。這個數目可以通過match_max來改變。(但太大的數目會降低匹配的效率)。如果patlist是full_buffer,則在收到match_max個字節而沒有相應的模式匹配成功時,執行full_buffer所對應的語句。不管是否使用了關鍵字full_buffer,丟棄的字符都會被寫到expect_out緩沖區中。如果patlist是關鍵字null。並且空是有效字符(通過remove_nulls指令訪問),如果輸出是一個單個的ASCII碼0,那麼null相對應的語句將被執行。通過glob或是regexp模式是不能來匹配0字符。

在匹配字符串時(或是遇到文件尾,或是緩沖區滿full_buffer),任何匹配的或是前面沒有匹配的輸出都會被保存在expect_out緩沖區中。匹配到的9個字符分別被放到expect_out(1,string)至expect_out(9,string)中。如果在模式前使用了-indices選項,那麼,這10個字符的開始字符和結尾字符在字符串中的位置被分別存放在變量expect_out(X,start)和expect_out(X,end)中。其中X是自然數(應該是0到9)。0(expect_out(0,*))是指整個匹配的字符串,它可以用於glob模式,也可以用於regexp模式。例如:如果一個進程的輸出為“abcdefgh\n”,那麼expect “cd”的執行結果和下面的代碼執行結果是一樣的。
set	expect_out(0,string) cd
set	expect_out(buffer) abcd
“efgh\n”被丟棄到輸出緩沖區了。如果一個進程的輸出是”abbbcabkkkka\n”,那麼expect –indices –re “b(b*).*(k+)”的執行結果和下面語句的執行結果是相同的。
set expect_out(0,start) 1
set expect_out(0,end) 10
set expect_out(0,string) bbbcabkkkk
set expect_out(1,start) 2
set expect_out(1,end) 3
set expect_out(1,string) bb
set expect_out(2,start) 10
set expect_out(2,end) 10
set expect_out(2,string) k
set expect_out(buffer) abbbcabkkkk
“a\n”被丟棄了輸出緩沖區中了。含有”*”(和-re “.*”)的模糊匹配的模式會清空輸出緩沖區,不再讀取從進程中輸出的字符。一般情況下,匹配的輸出會被Expect的內部緩沖區丟棄.可以通過在模式前加上-notransfer選項來避免被丟棄.這個選項在實驗時非常有用(為了方便,可以簡寫成-not).與匹配輸出相對應的spawn id被存儲在expect_out(spawn_id)中.

-timeout 選項使得Expect使用選項後面的數值做為超時時間,而不是timeout變量中設置的時間.
默認情況下,設定的模式只與當前進程的輸出進行匹配.-i選項使得指定spawn_id或是spawn_id列的輸出與下面列出的所有模式進行匹配(直到下一個-i選項為止).spawn_id列要麼是用空格分隔的一列spawn_id,要麼是用變量存儲的這要一列spawn_id.例如,下面的例子中,當前進程與”connected”進行匹配,由變量$proc2指定進程與”busy”,”failed”,”invalid passowrd”進行匹配。
expect {
-i $proc2 busy {puts busy\n ; exp_continue}
-re "failed|invalid password" abort
timeout abort
connected
}
全局變量any_spawn_id的值是在當前expect中所有-i選項定義的spawn_id進程列的總和.它用來使這些spawn_id進程列與模式進行匹配。在一些-i選項中可能只給出了spawn_id列,但沒有給出相應匹配模式.(例如,-i選項緊接下來就是另一個-i選項).那麼這些spawn_id列將會去匹配與any_spawn_id相對應的模式.

-i 選項還可以定義一個全局變量,裡面存儲著spawn_id列.當變量內容發生變化時,它會被重新讀取.這樣就可以在程序執行的時候改變I/O源.以這種方式提供的spawn_id被稱為”indirect spawn_id”.

break和continue使流程(例如:for結構,proc函數)按照正常的順序執行.exp_continue使expect繼續執行而不是像通常一樣返回.這對於避免explicit loops(不執行以後有語句,直接進入下一次循環)和重復的語句很有用.下面的例子是一個自動rlogin的代碼片斷.exp_continue的使用避免了在rlogin揭示輸入密碼的時候的再寫一個重復的expect語句.(需要等待第二次提示)
expect {
Password: {
stty -echo
send_user "password (for $user) on $host: "
expect_user -re "(.*)\n"
send_user "\n"
send "$expect_out(1,string)\r"
stty echo
exp_continue
} incorrect {
send_user "invalid password or account\n"
exit
} timeout {
send_user "connection to $host timed out\n"
exit
} eof {
send_user \
"connection to host failed: $expect_out(buffer)"
exit
} -re $prompt
}
例如,下面的代碼使用戶可以在任務完全自動化的情況下,還能引導人機交互.這種情況下,終端被設置成原始狀態.如果按下”+”,那麼一個變量的值增加,如果按下”P”,那麼向進程發送幾個回車符,或是以其它的方式回應一下.如果按下”i”,那麼用戶就會從腳本那兒把控制權收回,來與進程進行交互.在每個情況下,exp_continue都使在執行完當前的動作之後,繼續執行模式匹配.
stty raw -echo
expect_after {
-i $user_spawn_id
"p" {send "\r\r\r"; exp_continue}
"+" {incr foo; exp_continue}
"i" {interact; exp_continue}
"quit" exit
}
默認情況下,exp_continue會重置超時時間.如果以帶有-continue_timer選項的方式執行exp_continue的話,超時時鐘不會重新啟動.
Expect_after[expect_args]
它和expect_before的工作方式相同.在expect和expect_after能同時匹配的情況下.進程與expect命令下面的模式進行匹配.想了解更多的信息請參見expect_before.
Expect_background [expect_args]
它和expect有一樣的參數列表.但不同的是它是立即返回.一旦有新的輸入到達時就開始進行模式匹配,timeout和default兩個模式對於expect_background來說沒有意義,它們會被隱含忽略.否則,expect_background會像expect一樣調用expect_before和expect_after的模式匹配.
當expect_background在執行模式匹配時,對應於這個spawn_id的後台進程將被阻塞.當執行完成時,後台進程被解開.在後台進程被阻塞期間,還可以在前台以同樣的spawn_id執行一個expect腳本.但在非阻塞情況下是不可能這樣做的.在用同一個spawn_id聲明一個新的expect_background時,前一個就會被自動刪除.聲明一個沒有匹配模式的expect_background將會使相應的spawn_id失去在後台匹配模式的能力.
Expect_before [expect_args]
它和expect具有相同的參數列表.但不同的是它立即返回.相同spawn_id最近的expect_before下的匹配模式會自動隱含的加載到下面的expect命令中.如果其中一個模式匹配成功了,就好像匹配的模式是列在expect命令本身下面一樣.如果expect_before和expect的模式同時匹配,那麼將使用expect_before.如果沒有相應的匹配列出來,那麼這個spawn_id將不進行任何模式匹配的動作.
除非使用-i選項強制聲明,否則expect_before的模式將與執行expect_before命令時對應的spawn_id的進程輸出進行匹配(而不是有模式匹配成功時的spawn_id).-info選項會返回當前模式的詳細信息.默認情況,它會報告當前的spawn_id的信息。也可以通過指定spawn_id來顯示指定spawn_id的信息.
例如: expect_before –info –I $proc
這樣最多返回一個spawn_id的詳細信息. The flag -indirect suppresses direct spawn ids that come only from indirect specifications.
-all選項使expect_before報告所有spawn_id的信息,而不是單個spawn_id的信息。
expect_tty [expect_args ]
和expect的用法很像,但它是從/dev/tty讀取字符串(例如:用戶的擊鍵)。默認情況下,讀是工作在精加工緩沖模式下的。因此,每行之後必須以回車結尾,這樣expect才能分別識別它們。讀模式(例如行緩沖,等等)可以通過stty命令更改(參見下面的stty命令)
expect_user [expect_args]
和expect的用法很像,但它是從stdin(標准輸入)讀取字符串(例如:用戶的擊鍵)。默認情況下,讀是工作在精加工模式下的。因此,每行之後必須以回車結尾,這樣expect才能分別識別它們。讀模式(例如行緩沖,等等)可以通過stty命令更改(參見下面的stty命令)。
Fork
創建一個新進程。這個新進程是當前進程的完整拷貝。成功時,會返回0給新進程,返回新進程的ID給當前進程。失敗時(失敗的原因可能是資源匮乏,如交換分區,內存不足等),返回一個-1給當前進程,沒有新進程創建。復制的新進程和它的父進程一樣通過exit命令退出。復制的新進程允許寫日志文件。如果不屏蔽大多程序的debugging(調試)和logging(寫日志)功能,結果(個人認為:輸出結果或是日志)看起來會顯得有點混亂。在多個用戶的情況下,即使是很短暫的pty執行結果,看起來也會很讓人混亂迷惑。因此,在監視某個進程(個人認為是執行spawn)之前執行fork更好一點。
interact [string1 body1] ... [stringn [bodyn]]
返回當前進程的控制權給用戶。所以擊鍵會被傳給當前進程(就像平時操作一樣)。當前進程的stdout和stderr也會返回(個人認為:可能在腳本執行時,標准輸出和標准錯誤輸出是被重定向到Expect的,因為執行spawn之後,expect會等待進程的輸出,包括錯誤輸出)。String-body被指定為參數。在這種情況下,當有指定的string輸入時,對應的body就會被執行(默認情況,string不會被傳給當前進程)。如果沒有最後的body部分,那麼將執行interact命令。如果整個interact語句參數過長,超過一行,這些參數會用反斜線連接,分隔在多行,這樣避免了語句在執行時被隔斷。這種情況下,在Tcl進行語法解釋的時候會忽略這些反斜線,把這多行做為一條語句來執行。例如,下面的代碼舉例說明了以string-body方式執行interact命令。String-body是這樣設定的:當你按下Ctrl+Z時,Expect 將掛起,按下Ctrl+A時,用戶將會看到屏幕顯示“you typed a control A”,並且也向當前進程發送一個Ctrl+A。當用戶按下$時,用戶會看到屏幕上顯示系統日期。按下Ctrl+C 時Expect將退出。如果輸入”foo” ,用戶將在屏幕上看到“bar”,如果輸入~~,那麼Expect解釋器交互執行。
set CTRLZ \032
interact {
-reset $CTRLZ {exec kill -STOP [pid]}
\001 {send_user "you typed a control-A\n";
send "\001"
}
$ {send_user "The date is [exec date]."}
\003 exit
foo {send_user "bar"}
~~
}
在string-body中,字符是按string在string-body中出現的順序匹配的。
在不清楚余下的字符是什麼的情況下,只是部分匹配的字符是不會被發送到當前進程的。如果在獲得了余下的字符之後,整個字符串沒有相應的string-body可以匹配(也就是說整個字符串在string-body中,沒有對應相同的string),除了上面說的匹配字符外,也沒有其他更多的匹配(個人理解:比如整個字符xxxbbccada.第一次提到的匹配字符xxxbb,string-body中有兩個對應的string:string1=xxxbb,string2=xxxbbcc,那麼也就是說整個字符是沒有相應的string與之匹配,如果只有string1,沒有string2,那也就是“沒有更多的匹配”,只有xxxbb會發送到當前進程,如果存在string2,那麼我們最好把string2放在string1前面,這樣可以先在匹配string2,如果輸出字符串中,沒有相應的xxxbbcc,然後再去匹配string1。也就是說把“最大匹配”放在前面),那麼只有匹配的字符會發給當前進程。因此,我們可以把“部分匹配”放在後面,如果整個字符(或是“最大字符”)匹配失敗,我們再進行“部分匹配”。默認情況下,string匹配必須是精確完全匹配。(與之相反,expect命令默認使用glob-style模式)。-ex選項保證那些可能被解釋成interact選項的string能被正確執行。任何以”-”開頭的string都需要使用-ex。(所有以”-”開頭的字符將被做為選項)

-re選項強制string按regexp模式解釋。這種情況下,像expect會把它的輸出存儲在變量expect_out裡面一樣,interact匹配的字符串也會在存儲在變量interact_out中。-indices選項的作用也和expect中的一樣。Eof模式列出了在遇到文件尾的時候要執行的語句。一個單獨的eof模式可能跟在-output選項後面,這樣當寫輸出遇到文件尾的時候,就會觸發eof模式,執行相應的語句。默認的eof行為是返回,所以執行interact命令時,在遇到文件尾就是返回。Timeout模式介紹了超時(以秒為單位)的概念,並列出了(超時)連續數秒沒有讀取到字符後的執行語句。Timeout作用於最近指定的進程。**這裡沒有默認的timeout,特殊變量timeout(expect命令裡面使用的)對這裡的timeout模式沒有影響。**例如,下面的命令可以用於自動退出用戶,他們在一小時之內沒有輸入任何字符,卻一直收到系統消息。
interact -input $user_spawn_id timeout 3600 return –output $spawn_id
如果模式為關鍵詞null,而且null是允許的(通過remove_null命令),則在輸出中如果出現單個的ASCII 0,那麼null對應的語句將被執行。在glob和regexp模式下是不可能完成的。在模式前加上-iwrite選項,將會把匹配成功(或是遇到文件尾)的進程的spawn_id賦值給變量interact_out(spawn_id)。Break和continue會使控制結構(for循環,子函數等等)按照正常的方式運行,但return會使interact把信息返回給它的調用函數。Inter_return會使它的調用函數返回。例如,如果一個子函數foo調用了inter_return,在執行inter_return時,子函數foo會返回。(這就是說,當interact交互式調用解釋器時,如果輸入return,那麼交互還將繼續,如果輸入inter_return,那麼interact將返回)

在interact執行過程中,終端工作在“原始狀態”下,這樣所有字符都將發送給當前進程。如果當前進程沒有捕獲到工作流程的信號,那麼按下Ctrl+Z會使其中止。如果想重啟這個進程,可以給它發送一個“繼續”信號(如執行:"kill -CONT <pid>"),如果你真想給當前進程發送一個“中止”信號,你可以考慮先監視csh,然後再啟動你的程序。也就是說,如果你想發送中止信號給Expect,首先要調出解釋器(可能是按一下ESC鍵),然後按下Ctrl+Z。

為了避免進入解釋器,交互式的執行命令,string-body可以用做“速記”,當string-body對應的body執行的時候,使用的前一個終端模式。為了程序的執行效率,默認情況下,終端使用原始狀態。-reset使終端恢復到interact執行以前的狀態(總是“精加工”狀態)。注意的是,在進行終端模式轉換的時候,此時輸入的字符可能丟失(在一些系統上,會出現這種糟糕的現象)。最好在你必須使用”精加工”模式再使用-reset選項。-echo選項使與模式進行匹配的字符同時也被發送給產生這些字符串的當前進程,就好像是當前進程讀取到他們一樣。這在當用戶希望在執行某些指令需要看到回顯的時候非常有用。
如果回顯了一個模式,但最終沒有匹配成功,這些字符會被發送到監視的進程,如果監視的進程再把它們顯示出來的話,那麼用戶將會看到他們兩次。-echo可能僅僅適合於當用戶不可能不完成模式匹配的情況。例如:下面是摘自於rftp,一個遞歸式ftp腳本,用戶被提示輸入”~g,~p,~l”,以便遞歸的”獲得,上傳,查看”當前路徑。這些字符和常規的ftp命令相差太遠,用戶除非出錯,否則基本上不會打出”~”後面跟有某些字符的情況。這種情況下,他們就可能會忽略了正確的結果。
interact {
-echo ~g {getcurdirectory 1}
-echo ~l {getcurdirectory 0}
-echo ~p {putcurdirectory}
}
-nobuffer選項會把進行模式匹配的字符發送給輸出進程,就像這些字符是被讀取的一樣。這在你想讓進程回顯模式的時候非常有用。例如,下面的代碼監視了哪個用戶在撥叫(一種Hayes模式的Modem),每次都會在腳本的日志文件中後面看到一個”atd”。
proc lognumber {} {
interact -nobuffer -re "(.*)\r" return
puts $log "[exec date]: dialed $interact_out(1,string)"
}
interact -nobuffer "atd" lognumber
在交互過程中,log_user的前一個值被忽略了。特別需要說明的是,interact會強制使他的輸出記錄成日志(輸出到標准輸出),因為它認為用戶不希望沒有任何回應的交互。-o選項使下面的key-body模式應用於當前進程的輸出(也就是說用當前進程的輸出來匹配模式)。這對於處理像”在一個telnet會話中輸入很多錯誤字符(個人認為:非命令或是選項字符)”的情況非常有用。

默認情況下,interact希望用戶對標准輸入進行寫操作,對標准輸出進行讀操作。-u選項通過指定進程名(通常是指定一個spawn_id)來使此進程的用戶與其他進程進行interact(交互)。這就使兩個毫不相差的進程通過這樣一個聯系連接起來。為了協助調試,Expect的調試信息經常會輸出到標准錯誤輸出(或是是標准輸出,為了記錄日志和調試信息)。同樣,解釋器也會交互的從標准輸入讀取字符。例如:下面的代碼,建立了一個登錄進程,它呼叫用戶,然後使雙方連接在一起。當然其他進程也可以取代這裡的login進程。一個腳本,允許在不提供用戶名與密碼的情況下正常工作。
spawn login
set login $spawn_id
spawn tip modem
# dial back out to user
# connect user to login
interact -u $login
為了發送輸出給多個進程,必須使用-output選項指定spawn_id列表。同樣,要給多個進程輸入字符,需要使用-input選項(-input和-output,還有expect中的-i選項都支持列表,除了特殊變量any_spawn_id在interact命令中無效,在expect中有效)。
所有接下來的選項或字符串(或模式)對當前的輸入有效。直到下一個input選項為止。如果沒有-input選項,-output選項暗含表示”–input $user_spawn_id –output”(在不含有-input選項的模式中也一樣)。如果指定了一個-input選項,那麼它將覆蓋$user_spawn_id,如果出現第二個–input選項,那麼它將覆蓋$spawn_id,還有可能會指定更多的-input選項。
這兩個暗含的輸入進程把它們的輸出默認分別把$spawn_id和$user_spawn_id作為它們的輸出(做了調換)。如果-input選項後面沒有-output,那麼這個進程的輸入將會被忽略。-i選項介紹了一種當沒有使用-input或是-output選項時的替代方式。-i選項暗含一個-o選項。
使用“間接”spawn_id列可以改變交互進程 (“間接”spawn_id列已經在expect命令裡面講過) 。“間接”spawn_id列可以通過-i,-u,-input或是-output選項指定。
interpreter [args]
使用戶交互的輸入Expect或是Tcl命令,每個命令的結果都會被打印出來。
Break和continue會使控制結構(for循環,子函數等等)按照正常的方式運行,但return會使interact把信息返回給它的調用函數。Inter_return會使它的調用函數返回。例如,如果一個子函數foo調用了inter_return,在執行inter_return時,子函數foo會返回。其他命令使interpreter繼續提示輸入新的命令。默認情況下,提示包含兩個整數。第一個表示the depth of evaluation stack嵌套的層數(也就是Tcl_Eval被調用了多少次)。第二個參數是Tcl的history identifier歷史指針。提示符可以通過定義一個叫做”prompt1”的子函數來設置,這個子函數的輸出會成為下一個提示符。如果一條語句中包含半開的(也就是一個,不是一對兒)引號,大括號,中括號或是小括號,那麼下一個提示符會被放在新一行。第二個提示符同樣也可以通過定義一個叫做”prompt2”的子函數來設置。在interpreter執行過程中,終端使用“精加工”模式,即使它的調用函數使用的是“原始中”模式。如果在沒有使用-eof選項的情況下,標准輸入被關閉,那麼interpreter就會返回。如果使用了-eof選項,那麼將調用下一個參數。
log_file [args] [[-a] file]
如果指定了文件名,那麼log_file命令會把會話的記錄寫入文件(從執行這條語句開始),如果沒有給定任何參數,那麼log_file命令會停止記錄。前面的日志文件都將被關閉。不指定文件名,還可以通過-open或是-leaveopen選項來指定Tcl文件描述符,這和spawn命令的用法一樣(參見spawn命令)。-a選項強制把log_user命令產生的輸出記錄到日志。默認情況下,為了在一次會話中能很方便的多次關閉日志記錄,log_file命令會把輸出信息添加到文件尾,而不是覆蓋原來的內容。如果想覆蓋原來的內容,可以使用-noappend選項。-info選項使log_file命令返回關於最近的non-info(非info選項)參數的描述。
log_user -info|0|1
默認時,send/expect對話會被記錄到標准輸出中,可以通過log_user 0來禁止,通過log_user 1來恢復。輸出到日志文件維持不變。-info選項使log_user命令返回關於最近的non-info(非info選項)參數的描述。
match_max [-d] [-i spawn_id] [size]
這個命令定義Expect內部使用的緩沖區大小。如果沒有參數,返回當前大小。如果使用-d選項的話,將緩沖區設置為默認大小(初始的默認大小是2000 Bytes)。如果使用了-i選項,那麼設置的是對應於spawn_id的進程的緩沖區大小。否則設置的是當前進程的。
overlay [-# spawn_id] [-# spawn_id] [...] program [args]
終止當前的Expect程序,執行program args。一個連字符沒有指定參數,那麼連字符將被放到命令之前,就像它是一個登錄Shell一樣。除了那些在命令行中做為參數指定的spawn_id外,其他將全部被關閉。這些在命令行中指定的spawn_id將被重定向到指定的文件描述符。這些spawn_id被重定向到文件描述符是為了新程序來繼承。例如,下面的命令運行chess程序,而且允許當前程序--chess master(chess控制者)來控制。
overlay -0 $spawn_id -1 $spawn_id -2 $spawn_id chess
雖然它犧牲了執行程序化交互的能力,因為Expect已經失去控制權,但還是要比”interact –u”更有效率。注:在這裡,沒有提供控制終端,因此,如果你斷開或是重定向了標准輸入,那麼控制作業的程序(Shell,login等等)將不能正常訪問。
parity [-d] [-i spawn_id] [value]
定義parity是否需要與當前監視的進程的輸出分隔開。如果設為0,則是分隔開,否則將不分開。如果沒有參數的話,將返回當前值。-d選項將parity設置為默認的值(初始默認值為1,不分開)。-i選項用來指定需要設置parity值的,對應於spawn_id的進程。否則設置當前進程的parity值。
remove_nulls [-d] [-i spawn_id] [value]
此命令用來定義null在匹配模式或是存儲到變量expect_out或是interact_out之前,是否需要與監視進程的輸出分隔開。如果設為1,則分開,如果為0,則不分開。沒有參數,返回當前值。-d選項用來把它設為默認值(初始默認值為1,分隔開).-i選項用來設置對應於spawn_id的進程的remove_nulls的值。如果沒有使用-i選項,那麼將設置當前進程的remove_nulls的值。不管null是否被分隔,Expect都會把它們記錄到日志和標准輸出中。
send [-flags] string
發送字符串給當前進程。例如” send “hello world” ”會向當前進程發送“hello空格world回車”(Tcl提供了一個類似於printf的命令,它可以構建任意復雜的字符串)。盡管工作在行緩沖模式下的程序直到遇到回車的時候才會讀到這些字符,但這些字符還是在輸入的時候就被立即發送出去了。回車被表示為”\r”。
--選項使下面的參數做為字符串而不是選項。所有的字符串,不管它是否像選項,都可以前綴一個”--”。這提供了一種安全機制:不會再去費力區分很像選項的字符串到底是什麼了。(所有以”-”開頭的字符串將作為選項使用)。
-i選項指定字符串將發往對應於spawn_id的進程。如果指定的spwan_id是user_spawn_id的話,而且終端工作在原始狀態下,那麼字符串中的換行將被轉換為回車換行,以使它們看起來像是工作在“精加工“狀態下。-raw選項禁止這種轉換。-null選項用來發送null字符。默認情況是發送一個null字符。可以通過設定一個整數來確定發送多少個null字符。-break產生一個中斷條件。這只有對使用spwan_open命令打開tty設備對應的spawn_id時才有效。如果你監視了類似於tip的程序,那麼你應當遵循tip的規則來產生一個中斷。-s選項強制發送速度變慢。這是避免計算機輸入時,會出現輸入緩沖區溢出。因為某些輸入緩沖區是以人的輸入速度為標准設計的。輸出的速度由變量send_slow的值來限定的。它帶有兩個參數。第一個是一個整數,它用來描述自動發送的字節數。第二個參數用來設定發送的時間間隔。例如:set send_slow{10 .001} 將使”send -s”在每發10個字符後停1毫秒,然後再發10個字符。-h選項使發送模擬人的擊鍵速度。每個字符之間的時間間隔也像人手敲擊間隔。(這種算法是基於WeiBull規則,它帶有修正值以適應特殊情況)這種輸出的速度通過變量send_human中的5個參數來設定。前兩個參數是字符敲擊之間的平均時間,第一個是默認使用時間。第二個是輸入一個詞後的停頓時間。以模擬這種轉變過程中(從連續輸入字符到輸入一個詞再輸入另一個詞的轉變)的微小停頓。第三個參數是穩定度,.1表示非常不穩定,1表示相對不穩定,10表示非常穩定。極限是0和無限大。最近兩個參數分別指定了最小和最大的間隔時間。它們用來修正最終時間。如果最小和最大有大量的取值范圍,最後的平均時間可能會和給定的平均間隔時間有很大的差別。
例如:下面模擬了一個快速穩定的打字員
set send_human {.1 .3 1 .05 2}
send -h "I'm hungry. Let's do lunch."
下面模擬的更適合於一個經常有較大停頓的打字員
set send_human {.4 .4 .2 .5 100}
send -h "Goodd party lash night!"
注:錯誤是不可以模擬的。雖然你可以通過在send命令參數裡面嵌入錯誤和更正來設定錯誤更正模式。
這些發送null字符,設定中斷條件和強制發送變慢,模擬人的輸入的選項之間都是互斥的,只有最後的選項有效,還有需要說明的是,發送null字符和設定中斷條件選項是不能帶有其他參數的。

在第一個send前加一個expect命令是個不錯的主意,因為expect會等待進程開始,而send不會等待。在某些情況下,進程還沒有開始,你的第一個send已經發送完畢了,這樣你就會有把你的數據丟失的危險。在某些交互程序沒有初始化提示符的情況下,可以在send之前加一個延時。
# To avoid giving hackers hints on how to break in,
# this system does not prompt for an external password.
# Wait for 5 seconds for exec to complete
spawn telnet very.secure.gov
sleep 5
send password\r
exp_send是send的別名。如果你使用的是Expectk或是Tk環境下某些Expect的變體或是擴展,那麼Tk會把send做為與現在完全不同用途的命令。
send_error [-flags] string
用法很像send,除了它是把輸出發送到stderr(標准錯誤輸出),而不是當前進程。
send_log [--] string
用法很像send,除了它是把string發送給logfile。如果沒有logfile打開的話,那麼參數將被忽略。
send_tty [-flags] string
用法很像send,除了輸出是發往/dev/tty而不是當前進程。
send_user [-flags] string
用法很像send,除了輸出是發往標准輸出stdout而不是當前進程。
sleep seconds
使腳本停頓給定的秒數。Seconds是十進制數。在Expect停頓期間,中斷(或是在使用Expectk時的Tk事件)也會正常響應。
spawn [args] program [args]
創建一個執行program args命令的進程。它的stdin,stdou,stderr(標准輸入,標准輸出,標准錯誤輸出)都連到Expect。這樣它們可以被其他的命令讀取和寫入。執行close命令或是進程本身關閉這三個描述符之中的一個都將關閉這個連接。當用spawn開啟一個新進程時,這個進程的描述符將賦給變量spawn_id。Spawn_id指定的進程被認為是當前進程。為了有效的提供作業控制,可能需要讀寫spawn_id。User_spawn_id是一個全局變量,它指定了用戶的ID,例如:把user_spawn_id賦給spawn_id時,expect將以expect_user方式執行。Error_spawn_id也是一個全局變量。它包含了指向錯誤輸出的描述符。例如:當spawn_id賦了這個值的話,那麼send將以send_error方式工作。Tty_spwan_id也是一個全局變量,它包含了一個指向/dev/tty的描述符。如果/dev/tty不存在(例如在一個cron任務,at任務,或是batch腳本中,無人操作),那麼tty_spawn_id將不被賦值。可以通過下面的代碼得到驗證。
if {[info vars tty_spawn_id]} {
# /dev/tty exists
} else {
# /dev/tty doesn't exist
# probably in cron, batch, or at script
}
spawn命令會返回UNIX進程的ID。如果沒有監視進程,那麼返回0。變量spawn_out(slave,name)將保存pty從設備的名稱。默認情況下,spawn命令會回顯命令和參數,-noecho禁止回顯。-console選項使控制台的輸出重定向到監視的進程。但並不是所有系統都支持這一選項。Spawn內部使用pty。它像用戶的tty一樣初始化。它還將更進一步的初始化,以”完善”功能(對應stty(1))。如果設置了變量stty_init,它將以stty參數的形式執行進一步初始化。例如:“set stty_init raw”將會使以後監視的進程終端以原始方式啟動。–nottycopy選項將忽略(不執行)基於用戶tty的初始化。–nottyinit選項將忽略(不執行)“完善“的初始化。正常情況下,spawn的執行速度很快,如果你發現spawn執行的非常慢,它很有可能遇到了錯誤的pty,為了避免錯誤進程的干擾,在pty方面做了大量的測試(每個錯誤的pty會花費10秒鐘)。使用-d選項執行spawn命令會看到Expect是否工作在不穩定狀態的pty上面。如果不能殺死與這些pty對應的進程,那麼唯一的辦法就只有重啟。如果因為exec執行失敗(沒有program這個程序),造成program不能正常被監視,那麼在執行下一個interact或是expect命令時會返回一個錯誤信息。就像程序已經執行,然後產生了錯誤信息一樣。這是spawn正常的運行結果。在spawn執行的時候,如果執行fork復制進程,之後監視的進程和原來的Expect程序之間只能通過spawn_id互相通信。-open使下一個參數被解釋為Tcl文件描述符(也就是說由open命令返回的)。可以把它作為spawn_id使用,就像它是一個被監視的進程一樣(文件描述符將不再使用)。這讓你在不使用pty的情況下,監視原始設備,文件和管道。如果返回0,表示沒有相關聯的進程。當與監視進程的連接關閉之後,Tcl文件描述符也將關閉。-leaveopen選項用法很像-open選項,除了監視進程被關閉,Tcl文件描述符仍然保持打開之外。-pty選項打開一個pty,但不監視進程。返回0表示沒有相關聯的進程。Spawn_id還像以往一樣賦值。對應於pty slave的文件描述符賦給變量spawn_out(slave,name)。可以通過close-slave關閉pty slave。-ingnore選項定義了在監視進程過程中可以忽略的信號。如果沒有定義的話,那麼對應相應的信號將執行相應的動作。這裡的信號命名方式與trap命令中的一樣。只是這裡每個信號要分別使用一個單獨的標記。

strace level
使接下來的語句在執行之前首先被打印出來(Tcl的trace命令跟蹤變量)。Level定義了跟蹤幾層。例如,下面的語句執行Expect程序,只跟蹤4層調用,其他不管。
expect -c "strace 4" script.exp
-info選項使strace命令返回最近的非info選項參數的描述。
stty args
像內部命令stty一樣,改變終端的工作模式。默認情況下,控制終端是可以被訪問的。其他終端通過在文件尾添加”< /dev/tty..”,也可以被訪問(注意:這些參數不要組合成一個單一的參數)。終端狀態將作為命令結果返回。如果沒有請示終端狀態,而且控制終端可以訪問,那麼前一個“原始”狀態和顯示屬性將以一種命令可識別的方式返回。例如:參數raw或是–cooked使終端工作在原始狀態,而-raw或是cooked使終端工作在“精加工“狀態。參數-echo和–noecho分別設置終端為”回顯“和”不回顯“方式。
下面的例子演示了怎樣間斷性的關閉回顯。這可以用在那些避免將密碼嵌入到腳本中的自動執行程序(參見下面的Expect Hints關於這方面的討論)。
stty -echo
send_user "Password: "
expect_user -re "(.*)\n"
set password $expect_out(1,string)
stty echo
system args
把args傳給shell作為輸入,就好像是從終端輸入的一樣。Expect會等待直到shell執行結束。從shell的返回狀態的處理方式和exec處理它自己的返回狀態一樣。與exec不同的是:exec會把標准輸入和標准輸出重定向到腳本,而system不做重定向(除非在要執行的語句內部指定)。因此,它可以用於必須直接與/dev/tty對話的程序。同理,system的執行結果也不記錄到日志裡面。
timestamp [args]
返回一個時間戳。沒有參數的話,返回從epoch開始到現在的秒數。-format選項指定了一個要返回的時間字符串,返回的時候這個字符串中的相應字符將按照POSIX規則替換。例如:a%會被工作日的簡寫形式所代替(例如:Sat)。其他的如下:
%a abbreviated weekday name
%A full weekday name
%b abbreviated month name
%B full month name
%c date-time as in: Wed Oct 6 11:45:56 1993
%d day of the month (01-31)
%H hour (00-23)
%I hour (01-12)
%j day (001-366)
%m month (01-12)
%M minute (00-59)
%p am or pm
%S second (00-61)
%u day (1-7, Monday is first day of week)
%U week (00-53, first Sunday is first day of week one)
%V week (01-53, ISO 8601 style)
%w day (0-6)
%W week (00-53, first Monday is first day of week one)
%x date-time as in: Wed Oct 6 1993
%X time as in: 23:59:59
%y year (00-99)
%Y year as in: 1993
%Z timezone (or nothing if not determinable)
%% a bare percent sign
其他%形式沒有指定的,那麼相應的字符在返回時不改變。只支持C規范。
-seconds選項指定了一個整數。這個整數用來確定epoch。它是從epoch到現在的秒數。用epoch時刻的時間做為樣本,來格式化時間。如果沒有參數,那麼將使用當前時間作為樣本。
-gmt選項強制使時間戳的輸出使用GMT時區模式。如果沒有參數,使用當前時區。
trap [[command] signals]
在將來收到任何指定信號時,執行指定的命令。這個命令在全局范圍內有效。如果指定的命令不存在,那麼信號對應的執行動作就是返回。如果命令是SIG_IGN,那麼信號將被忽略。如果命令是SIG_DFL,那麼信號將執行系統的默認動作。Signals可以是一個信號,也可以是一組信號。信號可以以數字表示,也可以像signal(3)中定義的那樣以符號表示。可以省略前綴”SIG”。沒有參數的話,返回trap命令執行時的信號碼。-code選項在命令最初開始執行的時候,返回命令的返回碼,而不是返回Tcl的返回碼。
-interp選項使在命令執行的時候,而不是命令聲明的時候,用interpreter active給命令賦值。
-name選項返回trap命令當前被執行時的信號名。
-max選項返回最大的可以設置的信號碼
例如:”trap {send_user “Ouch”} SIGINT會在每次用戶按下Ctrl+C時打印出”Ouch”。默認情況下,SIGINT(按下Ctrl+C實現)和SIGTERM會使Expect退出,這是因為Expect起動時會默認執行trap exit {SIGINT,SIGTERM}。如果你用-D選項啟動了調試器,那麼SIGINT被重新定義為啟動交互調試器。這是因為默認執行了trap {exp_debug 1} SIGINT。這個跟蹤命令可以通過修改變量EXPECT_DEBUG_INIT中的值來改變。當然你也可以通過在腳本中增加相關的trap命令來覆蓋它們。特別說明的是,如果你寫了”trap exit SIGINT”,那麼這將覆蓋掉”trap {exp_debug 1} SIGINT(系統默認)。這在你阻止用戶進入調試器非常有用。如果你想在腳本運行的時候還能跟蹤調試器,而且還能定義自己的“SIGINT“信號對應執行指令。那麼你就用”if ![exp_debug] {trap my_CMD SIGINT}”。當必須出選擇時,你可以通過其它信號來調用調試器。Trap命令不允許覆蓋關於信號SIGALRM的對應執行動作。因為它是Expect內部使用的信號。Disconnect命令會把SIGALRM設置為SIG_IGN(忽略)。了解更多信息請參見signal(3)。
wait [args]
等待直到一個監視的進程中止。(如果沒有指定進程,則是當前進程)。Wait命令會返回一個4個整數列。第一個是監視進程的ID,第二個是對應的spawn_id,第三個如果為0表示成功,-1的話表示操作系統有有錯誤產生。如果第三個參數為0,第4個參數為監視進程的返回狀態信息。如果第三個參數為-1,第4個參數為系統設置的錯誤碼。全局變量errorCode也將被設置。在wait命令得到的返回信息中,還可能會出現其他元素。第五參數(可選的)表明了信息的級別。目前唯一可用的值是CHILDKILLED,這種情況下,接下來的兩個值分別是C-Style的信號名和一個短的文字性描述。-i選項指定要wait的進程是由spawn_id指定的進程(不是進程ID)。在SIGCHLD的句柄裡,通過指定spawn_id為-1,可以wait任何監視的進程。
-nowait選項使一旦成功執行wait之後,立即返回。這樣當進程退出(以後),不需要直接再顯式執行wait。進程就可以自己消失。通過使用”-i -1”,wait還可以用於等候一個復制的進程。不像它在其它監視進程裡面的用法,這裡它可以在任何時候執行。這時對於“哪個進程返回了”沒有控制,但可以通過檢測每個進程ID對應的返回值。

庫文件
Expect自動關聯兩個為Expect內建的庫文件。它們定義在變量exp_library和exp_exec_library定義的目錄裡。兩個目錄都是用來存放為其他腳本文件使用的有用文件。Exp_library包含有結構獨立的文件。Exp_exec_library包含了體系依賴的文件。依賴於系統,兩個文件夾可能都是空的。存在”$exp_exec_librara/cat-buffer”與否表明你的/bin/cat是否工作在默認的緩沖模式下。

漂亮的格式化顯示
規范化Expect腳本文件的格式是可行的。假設支持Expect的vgrind功能插件已經安裝。你可以這樣執行:vgrind –lexpect 腳本名

例子
也許手冊沒有很直接的告訴你怎麼把手冊上的例子放到一起應用。我還是鼓勵你把Expect目錄下的例子全部嘗試一遍。其中的一些是真實的程序。另外的一些也展示了一定的技巧。當然其中一些只是hacks。INSTALL文件中有一個程序的簡介。Expect也是很有用的。其中的一些規則使用的是較早版本的Expect。其中的原理仍然有效,只不過現在可能又形成了比上面介紹的更多的細節。

警告
擴展屬性可能會與Expect命令有沖突。例如,在Tk中,send被定義為與現在完全不同的用途。同理,很多其他命令在Tk中是以exp_xxx方式來使用的。以exp,inter,spawn和timeout開頭的命令和變量是沒有別名的。如果你想保證兼容性,必須使用擴展的命令名稱。 Expect提供了相對自由的作用范圍。特別說明的是,從命令行為Expect程序讀取的變量的賦值首先被當前作用域截取,如果沒有相應的變量,則在全局中查找。例如,這避免了在每個執行expect的進程裡面放上“global timeout”。另一方面,變量一般是以局部方式定義的(除非使用一個“全局命令“)。引起的最普遍的問題就是當在一個進程內部執行spawn命令的時候,在這個進程外部,spawn_id不再存在,所以由於作用范圍的問題,使監視的進程不能再訪問。在這樣的進程裡,可以加上一個全局變量spawn_id。如果你不能啟用多進程監視(你的系統不支持選擇,也不支持poll,或是類似的功能),Expect只能一次控制一個單個的進程。這種情況下,不要試圖設置spawn_id,也不要在一個監視進程運行的時候通過exec執行新的程序。這樣就是說,你不能用Expect來同時監視多個進程。

終端參數對腳本也有很大的影響。例如,如果腳本想要回顯,但如果終端關閉了回顯,那麼腳本執行將不能實現預期的結果。就是因為這樣,Expect強制“完善“的終端模式。不幸的是,這可能對其他某些程序來說不太方便。例如,Emacs想改變常見的繪制規則:把多行映射到多行,而不是回車換行分隔的多行,並且關閉回顯,這樣可以使用戶用Emacs編輯輸入。但不幸的是,Expect不能做到這些。
你可以使Expect不覆蓋終端的默認參數,不過你要為這種環境寫腳本要非常小心。在Emacs的例子中,要避免做依賴於回顯或是換行的工作。帶有很多參數的命令被連成一列(expect變量和interact),它需要一個標記來判斷它是一個參數還是多個。當本來是一個參數的命令中有很多的”\n”,而且沒有空格的時候,Expect將不能正確判斷。這聽起來好像完全不可能,然而-nobrace可以把一個參數強制按一個參數來解釋。這可以應用在由機器產生的Expect碼中。
Copyright © Linux教程網 All Rights Reserved