測試環境為 CentOS 7 x86_64 虛擬機
Expect 可以預先編寫與 Linux shell 的終端視窗 (當前的行程 process) 所顯示的字串來產生相對應的動作.跟 TeraTerm 的 Macro 類似 – https://benjr.tw/20386
須先安裝 expect 套件.
[root@localhost ~]# yum install expect
直接來看一個範例:
當使用者輸入 Hi 或是 How are you 會回應相對應的字串,然後繼續執行,除非是輸入 Bye 才會返回 terminal,如果使用者鍵盤在10秒內沒有輸入任何值時也會返回 terminal (重置超時計時器 timeout 10 秒).
[root@localhost ~]# cat test.exp #!/usr/bin/expect set timeout 10 expect { "Hi" {send "Hi\n"; exp_continue} "How are you?" {send "Fine\n"; exp_continue} "Bye" {send "Bye Bye\n"; exit} }
參數說明:
- timeout – 如果使用者鍵盤或是其他程式執行沒有產生任何值,預設等待時間為 10秒即結束,可以使用 -1 代表沒有中斷時間.
- expect – 等待指定的 patterns 出現在 terminal (當前的行程 process) 上(使用者輸入或是其他程式執行時產生).
- send – 將字串發送到當 terminal (當前的行程 process) 上.
- exp_continue – 會繼續執行而不是返回 terminal (當前的行程 process),預設會重置超時計時器 timeout .
- exit – 退出 expect .
實際測試可以看到當使用者輸入 Hi 或是 How are you 會回應相對應的字串,然後繼續執行,最後輸入 Bye 才會返回 terminal.
[root@localhost ~]# chmod a+x test.exp [root@localhost ~]# ./test.exp Hi Hi How are you Fine Bye bye bye
自動登入 SSH
在 Linux 環境下使用 SSH 進行遠端連線,需要輸入密碼,除了可以透過把公鑰匯出 https://benjr.tw/301 或是透過指令 sshpass – https://benjr.tw/8287 ,另外一種方式就是透過 expect
下面範例使用 expect 透過遠端 ssh (免輸入密碼) 來執行指令.
[root@localhost ~]# vi ssh.exp #!/usr/bin/expect set timeout 10 set HOST [lindex $argv 0] set USER [lindex $argv 1] set PASS [lindex $argv 2] spawn ssh -o StrictHostKeyChecking=no $USER@$HOST expect { "password:" {send "$PASS\r"} } interact
參數說明:
- timeout – 如果使用者鍵盤或是其他程式執行沒有產生任何值,預設等待時間為 10秒即結束,可以使用 -1 代表沒有中斷時間.
- expect – 等待指定的 patterns 出現在 terminal (當前的行程 process) 上(使用者輸入或是其他程式執行時產生).
- send – 將字串發送到當 terminal (當前的行程 process) 上.
- $argv 0,1,2 – 執行 expect 後面所接的命令行參數.
- spawn – 產生新的行程 (process).
- interact – 把目前行程 (process)的控制權交還給使用者,沒有這一行就會退出並結束程式.
指令說明:
- ssh -o StrictHostKeyChecking=no $USER@$HOST
在 SSH Client 第一次登入時 SSH Server 會詢問是否接收公開金鑰 (SSH Client 需要輸入 yes),使用參數 StrictHostKeyChecking no 讓預設都為接受.
[root@localhost ~]# chmod a+x ssh.exp [root@localhost ~]# ./ssh.exp 192.168.95.205 root 111111 spawn ssh -o StrictHostKeyChecking=no root@192.168.95.205 root@192.168.95.205's password: Last login: Fri Jan 18 18:18:16 2019 from 192.168.95.205 [root@localhost ~]# exit logout Connection to 192.168.95.205 closed. [root@localhost ~]#
紀錄 expect 的執行結果
要紀錄 expect 的執行結果可以透過兩種方式 1. 使用 tee 雙向重導向指令可以同時將資料儲存成檔案並輸出到螢幕 (stdout). 2. 使用 Expect 所提供的 log_file 指令.
- tee
範例同上的 ssh.exp 但透過 tee 來同時把資料儲存成檔案並輸出到螢幕.[root@localhost ~]# ./ssh.exp 192.168.95.231 root 111111 | tee -a -i sshlog spawn ssh -o StrictHostKeyChecking=no root@192.168.95.231 root@192.168.95.231's password: Last login: Wed May 8 22:53:39 2019 from 192.168.95.228 [root@localhost ~]# ls anaconda-ks.cfg Documents initial-setup-ks.cfg Pictures Templates Desktop Downloads Music Public Videos [root@localhost ~]# ifconfig ens33 ens33: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1500 inet 192.168.95.231 netmask 255.255.255.0 broadcast 192.168.95.255 inet6 fe80::9e85:25f2:bd7:1ef3 prefixlen 64 scopeid 0x20<link> inet6 fe80::388e:11f5:7c79:dbff prefixlen 64 scopeid 0x20<link> ether 00:0c:29:7c:01:63 txqueuelen 1000 (Ethernet) RX packets 11914 bytes 16656168 (15.8 MiB) RX errors 0 dropped 0 overruns 0 frame 0 TX packets 4682 bytes 332316 (324.5 KiB) TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0 [root@localhost ~]# exit logout Connection to 192.168.95.231 closed.
tee 參數說明:
-a, –append
append to the given FILEs, do not overwrite
-i, –ignore-interrupts
ignore interrupt signals[root@localhost ~]# cat sshlog spawn ssh -o StrictHostKeyChecking=no root@192.168.95.231 root@192.168.95.231's password: Last login: Wed May 8 22:53:39 2019 from 192.168.95.228 [root@localhost ~]# ls anaconda-ks.cfg Documents initial-setup-ks.cfg Pictures Templates Desktop Downloads Music Public Videos [root@localhost ~]# ifconfig ens33 ens33: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1500 inet 192.168.95.231 netmask 255.255.255.0 broadcast 192.168.95.255 inet6 fe80::9e85:25f2:bd7:1ef3 prefixlen 64 scopeid 0x20<link> inet6 fe80::388e:11f5:7c79:dbff prefixlen 64 scopeid 0x20<link> ether 00:0c:29:7c:01:63 txqueuelen 1000 (Ethernet) RX packets 11914 bytes 16656168 (15.8 MiB) RX errors 0 dropped 0 overruns 0 frame 0 TX packets 4682 bytes 332316 (324.5 KiB) TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0 [root@localhost ~]# exit logout Connection to 192.168.95.231 closed.
- log_file
利用 Expect 所提供的指令 log_file 來將執行結果記錄到檔案.[root@localhost ~]# cat ssh.exp #!/usr/bin/expect set timeout 10 log_file sshlog set HOST [lindex $argv 0] set USER [lindex $argv 1] set PASS [lindex $argv 2] spawn ssh -o StrictHostKeyChecking=no $USER@$HOST expect { "password:" {send "$PASS\r"} } interact
log_file 可使用的參數
-noappend
預設不會把原資料覆蓋掉,會儲存至檔案後面 append ,如果要把舊資料覆蓋掉可以使用 -noappened .[root@localhost ~]# ./ssh.exp 192.168.95.231 root 111111 spawn ssh -o StrictHostKeyChecking=no root@192.168.95.231 root@192.168.95.231's password: Last login: Wed May 8 23:36:38 2019 from 192.168.95.228 [root@localhost ~]# ifconfig ens32 ens32: error fetching interface information: Device not found [root@localhost ~]# exit logout Connection to 192.168.95.231 closed. [root@localhost ~]# ./ssh.exp 192.168.95.231 root 111111 spawn ssh -o StrictHostKeyChecking=no root@192.168.95.231 root@192.168.95.231's password: Last login: Wed May 8 23:40:11 2019 from 192.168.95.228 [root@localhost ~]# ifconfig ens33 ens33: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1500 inet 192.168.95.231 netmask 255.255.255.0 broadcast 192.168.95.255 inet6 fe80::9e85:25f2:bd7:1ef3 prefixlen 64 scopeid 0x20<link> inet6 fe80::388e:11f5:7c79:dbff prefixlen 64 scopeid 0x20<link> ether 00:0c:29:7c:01:63 txqueuelen 1000 (Ethernet) RX packets 154224 bytes 217437655 (207.3 MiB) RX errors 0 dropped 0 overruns 0 frame 0 TX packets 61438 bytes 3818999 (3.6 MiB) TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0 [root@localhost ~]# exit logout Connection to 192.168.95.231 closed.
[root@localhost ~]# cat sshlog spawn ssh -o StrictHostKeyChecking=no root@192.168.95.231 root@192.168.95.231's password: Last login: Wed May 8 23:36:38 2019 from 192.168.95.228 [root@localhost ~]# ifconfig ens32 ens32: error fetching interface information: Device not found [root@localhost ~]# exit logout Connection to 192.168.95.231 closed. spawn ssh -o StrictHostKeyChecking=no root@192.168.95.231 root@192.168.95.231's password: Last login: Wed May 8 23:40:11 2019 from 192.168.95.228 [root@localhost ~]# ifconfig ens33 ens33: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1500 inet 192.168.95.231 netmask 255.255.255.0 broadcast 192.168.95.255 inet6 fe80::9e85:25f2:bd7:1ef3 prefixlen 64 scopeid 0x20<link> inet6 fe80::388e:11f5:7c79:dbff prefixlen 64 scopeid 0x20<link> ether 00:0c:29:7c:01:63 txqueuelen 1000 (Ethernet) RX packets 154224 bytes 217437655 (207.3 MiB) RX errors 0 dropped 0 overruns 0 frame 0 TX packets 61438 bytes 3818999 (3.6 MiB) TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0 [root@localhost ~]# exit logout Connection to 192.168.95.231 closed.
如果不想記錄全部資料,可以在需要紀錄的 expect 前面寫 log_file filename ,結束紀錄再用一次 log_file 即可.
[root@localhost ~]# cat ssh.exp #!/usr/bin/expect set timeout 10 set HOST [lindex $argv 0] set USER [lindex $argv 1] set PASS [lindex $argv 2] spawn ssh -o StrictHostKeyChecking=no $USER@$HOST log_file sshlog expect { "password:" {send "$PASS\r"} } interact log_file
使用 autoexpect 來產生 exp 檔
還有一種方式可以更簡單完成,只需要透過 autoexpect 來記錄並產新相對應的 exp 檔.
自動登入 SSH 執行 hostname 後結束.
[root@localhost ~]# autoexpect -f autossh.exp ssh root@192.168.95.205 autoexpect started, file is autossh.exp root@192.168.95.205's password: Last login: Fri Jan 18 18:29:37 2019 from 192.168.95.205 [root@localhost ~]# hostname -a localhost.localdomain localhost4 localhost4.localdomain4 localhost.localdomain localhost6 localhost6.localdomain6 [root@localhost ~]# exit logout Connection to 192.168.95.205 closed. autoexpect done, file is autossh.exp
不過紀錄得太詳細了,可能還需要修改後才能使用.
set timeout -1 spawn ssh root@192.168.95.205 match_max 100000 expect -exact "root@192.168.95.205's password: " send -- "111111\r" expect -exact "\r Last login: Fri Jan 18 18:29:37 2019 from 192.168.95.205\r\r ^[\]0;root@localhost:~^G^[\[?1034h\[root@localhost ~\]# " send -- "hostname -a\r" expect -exact "hostname -a\r localhost.localdomain localhost4 localhost4.localdomain4 localhost.localdomain localhost6 localhost6.localdomain6\r ^[\]0;root@localhost:~^G\[root@localhost ~\]# " send -- "exit\r" expect eof
修改後的內容:
set timeout -1 spawn ssh root@192.168.95.205 match_max 100000 expect -exact "root@192.168.95.205's password: " send -- "111111\r" expect "#" send -- "hostname -a\r" expect "#" send -- "exit\r" expect eof
參數說明:
- timeout – 如果使用者鍵盤或是其他程式執行沒有產生任何值,預設等待時間為 10秒即結束,可以使用 -1 代表沒有中斷時間.
- spawn – 產生新的行程 (process).
- match_max – defines the size of the buffer (in bytes) used internally by expect.
- expect – 等待指定的 patterns 出現在 terminal (當前的行程 process) 上(使用者輸入或是其他程式執行時產生).
- send – 將字串發送到當 terminal (當前的行程 process) 上.
- eof – The corresponding body is executed upon end-of-file.
[root@localhost ~]# ./autossh.exp spawn ssh root@192.168.95.205 root@192.168.95.205's password: Last login: Fri Jan 18 19:06:33 2019 from 192.168.95.205 [root@localhost ~]# hostname -a localhost.localdomain localhost4 localhost4.localdomain4 localhost.localdomain localhost6 localhost6.localdomain6 [root@localhost ~]# exit logout Connection to 192.168.95.205 closed.