expect-調試模式的使用
一、expect簡介
Expect是一種TCL擴展性的語言,主要用於完成系統交互方面的功能,比如SSH、FTP等,這些程序都需要手工與它們進行互動,而使用Expect就可以模擬人手工互動的過程,是一種自動的方式控制。
在使用shell編寫巡檢腳本的過程中,常使用expect工具與巡檢機器進行交互。通常我們在碰到交互邏輯無法往下進行的時候,這就需要使用到調試模式來具體分析。
二、安裝
配置好yum後,安裝命令如下
yum install expect
三、從一個簡單的腳本開始
1 #!/usr/bin/expect -d 2 #writed by ahao 3 #註意,在spawn中,如果要註釋的話,另外起一行,放在代碼的後面會有問題,這點與bash不一樣4 set timeout 10 5 spawn -noecho ssh -o StrictHostKeyChecking=no -l test 192.168.2.151 -p 22 6 #spawn命令是expect的初始命令,他用於啟動一個進程,之後所有操作都在這個進程中進行,如果沒有spawn,這個expect都無法進行 7 #StrictHostKeyChecking=no參數讓ssh默認添加新主機的公鑰指紋,也就不會出現出現是否繼續yes/no的提示了 8 expect "password:" {send "123456\r"} 9 expect "Last login" {send "echo test1\r"} #執行命令 10 expect "*\$*" {send "echo test2\r"} #執行命令 11 expect eof 12 #EOF(End Of File),表示"文字流"(stream)的結尾。這裏的"文字流",可以是文件(file),也可以是標準輸入(stdin),EOF是不可輸出字符,因此不能在屏幕上顯示。由於字符的ASCII碼不可能出現-1,因此EOF定義為-1是合適的。當讀入的字符值等於EOF時,表示讀入的已不是正常的字符而是文件結束符,但這適用對文本文件的讀寫 13 #也就是說eof通常定義為-1,在本文文件中數據都是以字符的ASCII代碼值的形式存放。我們知道,ASCII代碼值的範圍是0~255,不可能出現-1,因此可以用EOF作為文件結束標誌,如果處理時返回-1的值,那就eof
註:腳本中有4個expect過程
- 第一個expect:匹配到輸入密碼的提示,輸入密碼
- 第二個expect:輸入密碼後會有上次登錄的一個信息,匹配”Last login”後執行一個echo命令
- 第三個expect:執行echo命令完成後,匹配提示符”$”後再執行一個echo命令
- 第四個expect命令:匹配eof
整個執行過程如下:1-3expect過程分別使用不同顏色標識
[patrol@report-server db_bin]$ ./auto_login.sh expect: does "" (spawn_id exp4) match glob pattern "password:"? no expect: does " " (spawn_id exp4) match glob pattern "Last login"? no
expect: does " \r\nLast login: Sun Nov 25 20:35:43 2018 from 192.168.2.150\r\r\n" (spawn_id exp4) match glob pattern "Last login"? yes expect: does ": Sun Nov 25 20:35:43 2018 from 192.168.2.150\r\r\n" (spawn_id exp4) match glob pattern "*$*"? no expect: does ": Sun Nov 25 20:35:43 2018 from 192.168.2.150\r\r\necho test1\r\n" (spawn_id exp4) match glob pattern "*$*"? no |
為了對比分析,把調試模式關閉,整個輸出如下
[patrol@report-server db_bin]$ ./auto_login.sh |
四、過程分析
首先了解一下expect_out數組,expect_out數組專用與expect命令,裏面的元素包括:
expect_out(buffer)、expect_out(X,string)、expect_out(X,start)、expect_out(X,end)、expect_out(spawn_id)
上面的X字符表示從0-9的整數,具體如下
- expect_out(buffer)是一個看起來比較特殊的變量,這個變量中放置從上一次匹配到這一次匹配之間的所有返回,包括匹配字符本身。----重點
- expect_out(x,string):x是一個數字,從0到9,加起來一共是10個變量,其中0變量比較特殊,它裏面保存整個expect正則表達式中匹配到的所有內容,而從1開始到9的變量中,保存正則表達式中子模式的值,也就是括在()中的那部分內容,從左往右計算
- expect_out(x,start|end):x與上面的含義相同,這個變量的值是一個數值,以expect_out(1,start)為例,它裏面保存第一個子模式在expect_out(buffer)變量中匹配到的時候的索引值,比如第一個子模式匹配是在expect_out(buffer)中的第10個字符開始匹配到的,那麽這個變量的值就是10
結合上面的的第1個expect過程進行解析:
1 expect: does "" (spawn_id exp4) match glob pattern "password:"? no #不匹配,expect: does執行匹配動作,包括源字符串與需匹配的字符串,匹配是否成功,如果匹配成功,下面輸出相關信息與動作,這裏“ match glob pattern”匹配的是通配符表達式,如果使用正則表達式,會是類似如下輸出:match regular expression ".*#"?,此時expect_out(buffer)為空 2 [email protected]‘s password: #輸出 3 expect: does "[email protected]‘s password: " (spawn_id exp4) match glob pattern "password:"? yes #yes代表匹配 4 expect: set expect_out(0,string) "password:" #匹配到的字符串為”password” 5 expect: set expect_out(spawn_id) "exp4" #spawn_id進程為exp4 6 expect: set expect_out(buffer) "[email protected]‘s password:" #expect_out(buffer)變量的信息:[email protected]‘s password: 7 send: sending "123456\r" to { exp4 } #執行的動作,發送字符串到exp4
結合上面的第2個expect過程進行解析:
1 2 expect: does " \r\n" (spawn_id exp4) match glob pattern "Last login"? no #expect_out(buffer)變量為\r\n,在進行第2次expect的時候,expect_out(buffer)變量是不包含第一次匹配的字符串的 3 Last login: Sun Nov 25 20:35:43 2018 from 192.168.2.150 #上一個expect_send後的輸出 4 5 expect: does " \r\nLast login: Sun Nov 25 20:35:43 2018 from 192.168.2.150\r\r\n" (spawn_id exp4) match glob pattern "Last login"? yes #匹配了,下面開始相關信息的輸出與執行下一步的動作 6 expect: set expect_out(0,string) "Last login" #匹配到的字符串 7 expect: set expect_out(spawn_id) "exp4" 8 expect: set expect_out(buffer) " \r\nLast login" #expect_out(buffer)變量的值為“\r\nLast login” 9 send: sending "echo test1\r" to { exp4 } #發送命令到exp4
第3個expect分析:略
五、提一個問題
第二個expect和第三個expect順序調轉下,會是啥結果?如下代碼:
1 #!/usr/bin/expect 2 #writed by ahao 3 #註意,在spawn中,如果要註釋的話,另外起一行,放在代碼的後面會有問題,這點與bash不一樣 4 set timeout 10 5 spawn -noecho ssh -o StrictHostKeyChecking=no -l test 192.168.2.151 -p 22 6 #spawn命令是expect的初始命令,他用於啟動一個進程,之後所有操作都在這個進程中進行,如果沒有spawn,這個expect都無法進行 7 #StrictHostKeyChecking=no參數讓ssh默認添加新主機的公鑰指紋,也就不會出現出現是否繼續yes/no的提示了 8 expect "password:" {send "123456\r"} 9 expect "*\$*" {send "echo test2\r"} 10 expect "Last login" {send "echo test1\r"} 11 expect eof 12 #EOF(End Of File),表示"文字流"(stream)的結尾。這裏的"文字流",可以是文件(file),也可以是標準輸入(stdin),EOF是不可輸出字符,因此不能在屏幕上顯示。由於字符的ASCII碼不可能出現-1,因此EOF定義為-1是合適的。當讀入的字符值等於EOF時,表示讀入的已不是正常的字符而是文件結束符,但這適用對文本文件的讀寫 13 #也就是說eof通常定義為-1,在本文文件中數據都是以字符的ASCII代碼值的形式存放。我們知道,ASCII代碼值的範圍是0~255,不可能出現-1,因此可以用EOF作為文件結束標誌,如果處理時返回-1的值,那就eof
結果就是:第三個expect不可能匹配成功, expect_out(buffer)變量已不可能包含關鍵字”Last login”
expect-調試模式的使用