Linux – Expect

Loading

測試環境為 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 指令.

  1. 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.
    
  2. 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.
沒有解決問題,試試搜尋本站其他內容

發佈留言

發佈留言必須填寫的電子郵件地址不會公開。 必填欄位標示為 *

這個網站採用 Akismet 服務減少垃圾留言。進一步了解 Akismet 如何處理網站訪客的留言資料