expect是linux中的一個用來處理交互的命令,借助Expect,我們可以將交互過程寫在一個腳本上,使之自動化完成,形象的說,ssh登錄,ftp登錄等都符合交互的定義,下文我們首先提出一個問題,然后介紹基礎知四個命令.
Expect詳解
expect命令簡單說就是通過其內置的各種命令實現在交互式軟件中自動交互的工具,用在ssh時,可以配合spawn命令實現ssh的自動登錄,又因為可以在腳本中編寫判斷,賦值等邏輯,具有很高的靈活性.
問題:如何從機器A上ssh到機器B上,然后執行機器B上的命令?如何使之自動化完成?
四個命令:
Expect中最關鍵的四個命令是send,expect,spawn,interact。
send:用于向進程發送字符串
expect:從進程接收字符串
spawn:啟動新的進程
interact:允許用戶交互
1.send命令
send命令接收一個字符串參數,并將該參數發送到進程.
- expect1.1> send "hello world\n"
- hello world
2.expect命令
(1)基礎知識
expect命令和send命令正好相反,expect通常是用來等待一個進程的反饋,expect可以接收一個字符串參數,也可以接收正則表達式參數,和上文的send命令結合,現在我們可以看一個最簡單的交互式的例子:
- expect "hi\n"
- send "hello there!\n"
這兩行代碼的意思是:從標準輸入中等到hi和換行鍵后,向標準輸出輸出hello there.
tips:$expect_out(buffer)存儲了所有對expect的輸入,<$expect_out(0,string)>存儲了匹配到expect參數的輸入.
比如如下程序:
- expect "hi\n"
- send "you typed <$expect_out(buffer)>"
- send "but I only expected <$expect_out(0,string)>"
當在標準輸入中輸入
test
hi
是,運行結果如下:
- you typed: test
- hi
- I only expect: hi
(2)模式-動作
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 "That was unexpected\n" }
匹配到hi,hello,bye任意一個字符串時,執行相應的輸出,等同于如下寫法:
- expect {
- "hi" { send "You said hi\n"}
- "hello" { send "Hello yourself\n"}
- "bye" { send "That was unexpected\n"}
- }
3.spawn命令
上文的所有demo都是和標準輸入輸出進行交互,但是我們跟希望他可以和某一個進程進行交互,spawm命令就是用來啟動新的進程的,spawn后的send和expect命令都是和spawn打開的進程進行交互的,結合上文的send和expect命令我們可以看一下更復雜的程序段了.
- 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 ftp.uu.net上,并以二進制的方式下載服務器上的文件test.tar.gz,程序中有詳細的注釋.
4.interact
到現在為止,我們已經可以結合spawn、expect、send自動化的完成很多任務了,但是,如何讓人在適當的時候干預這個過程了,比如下載完ftp文件時,仍然可以停留在ftp命令行狀態,以便手動的執行后續命令,interact可以達到這些目的,下面的demo在自動登錄ftp后,允許用戶交互.
- spawn ftp ftp.test.com
- expect "Name"
- send "user\r"
- expect "Password:"
- send "123456\r"
- interact
解決方法,上文中提到:如何從機器A上ssh到機器B上,然后執行機器B上的命令?如何使之自動化完成?
下面一段腳本實現了從機器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
實例前言:前段時間將工作用的機器換成了 Ubuntu + 128G SSD + 8G內存的臺式機,終于走上了用linux辦公的道路,總的說來,Ubuntu用起來還是比我想象的順暢得多,其“apt-get式”的包管理方式,安裝軟件非常方便,apt-get install xxx 即可安裝,唯一的缺點就是銀行的“U盾”等不支持linux,所以,使用網銀時略有不便.
言歸正傳,在換臺式后公司還做了一件事就是統一了公司內的測試環境,將所有的測試環境機器統一到機房內用虛擬機的方式統一管理(皆大歡喜)。為了以后開發測試方便,項目組申請了多套測試環境,比如:開發環境:用于開發人員每次迭代自測時的環境。測試環境:穩定的和線上代碼同步的測試環境,通常用于演示或者對測試環境其他應用提供服務,除此以外,還有一套性能測試環境。這么多環境的問題就導致,每次需要更新環境:update 代碼,重啟tomcat時都需要ssh到某臺機器上。這其實是一個浪費時間的重復性勞動,通常情況,這種事情都可以使用代碼解決。這就是我們今天的主角:expect。
demo實例如下:
- #!/usr/bin/expect -f ##聲明使用哪種解析器解析該腳本,如果沒有安裝expect的話,使用apt-get install expect安裝
- if { [llength $argv] < 1 } { ##$argv為執行該腳本是的參數數組,判斷長度,以決定是否繼續.
- puts "Usage: $argv0 need ssh ip" //expect中的echo,System.out.println()
- exit 1
- }
- set envs [lindex $argv 0] ##將數組中的第一個參數賦值給 envs
- set timeout 30 //設置等待終端響應的超時時間為30秒
- if { $envs == 61 } { ##注意 if和{間有個空格,{和$envs間有個空格,等號前后有空格,61后有空格,}{間有空格!!!
- set ips xxx.xx.xxx.61
- }
- if { $envs == 141 } {
- set ips xxx.xx.xxx.141
- }
- if { $envs == 136 } {
- set ips xxx.xx.xxx.136
- }
- spawn ssh root@$ips ##執行ssh命令實現登陸
- expect {
- ##第一次登陸的時候,會詢問是否有在本地保存該密鑰,仔細的你應該發現,這里的匹配是用正則的,簡答說,只匹配connecting也是可以的。
- "Are you sure you want to continue connecting (yes/no)?" {send "yes\r"}
- "password:" {send "cdyanfa\r"}##保存過一次后,就會直接返回需要密碼的階段了。使用send 命令發送密碼。
- }
interact ##是Expect用來打開用戶與產生進程之間通信的命令,簡單說就是登陸以后將遠程服務器的終端保持在當前終端,而不是將遠程終端關掉,短短幾行命令就將每天重復N次的操作簡化成一行了,對于提高效率很有用.
新聞熱點
疑難解答