使用expect實現自動交互,shell命令行自動輸入,腳本自動化

背景

有需求,在容許命令或者腳本跳出交互行,須要進行內容輸入,但須要人手動輸入,不是很方便,此時能夠經過expect來實現自動互動交互。shell

expect是一個自動交互功能的工具,能夠知足代替咱們實際工做中須要從終端手動輸入某些內容來使得程序或命令繼續運行的目的。如安裝軟件是時的一些提示,ssh遠程主機執行命令時須要屢次輸入密碼的狀況。bash

安裝expect

  • 安裝依賴:yum install tcl -y
  • 安裝expect:yum install expect -y

一些基本的expect命令

  • spawn :啓動新進程,用於執行shell命令;
  • expect :從發起交互的命令的進程接受字符串,用於匹配咱們預想的字符串;
  • send :用於向發起交互的命令的進程發送字符串;
  • interact:容許用戶交互,即此命令後,交互將不會由expect進行,將交回給用戶;

示例

#!/usr/bin/expect

set timeout 30
set host "192.168.200.221"
set username "root"
set password "123456"

spawn ssh $username@$host ls
expect "password" {send "$password\r"}
expect eof
interact

#!/usr/bin/expect: 表示使用expect來解釋該腳本。ssh

set timeout 30: 表示設置超時時間,這裏是表示超時時間爲30秒,默認爲10秒,用於執行shell命令的時間,若是執行的shell命令時間較長(如傳輸文件),則須要設置長一點。ide

set username "root" : 表示設置並定義了變量username,變量值爲"root"。工具

spawn ssh $username@$host ls: 表示使用spawn來執行ssh $username@$host ls 命令,該命令只有在expect環境裏才能執行,因此直接在命令行輸入或沒有安裝expect則會報錯,它的主要功能是給它後面的shell命令運行進程加了個殼,進行傳遞交互的內容,注意,若是用引號將變量引發,將可能致使錯誤extra characters after close-quote...。oop

expect "password": 表示從spawn執行的命令的進程裏接受字符串,通常是彈出終端的交互行的標準輸入提示信息,如須要你肯定時的(yes/no?),須要你輸入密碼的(...password:)。這裏由於ssh命令的交互內容是叫你輸入密碼,交互提示的內容有password,因此這裏匹配password。spa

end $password\r表示當expect匹配成功,就把$password發送給spawn執行的命令的進程,完成交互,至關於手動輸入$password,這裏的\r表明回車,也可使用\n。.net

expect eof: 表示結束expect,讀取到文件結束符 ,當spawn發送指令到終端執行時在返回時被expect捕捉時,在起始會有一個eof,就比如在shell中 cat >>file <<EOF... EOF同樣,在結束時也要有eof;expect eof有時間限制,即咱們設置的超時時間,默認10秒,不過可能出現的問題是,若是是在傳輸一個大文件,可能在文件還沒傳輸完成便斷開了命令執行,此時須要設置超時時間長一點,或將expect eof改爲expect -timeout -1 eof命令行

interact: 執行完命令後,控制權交互控制檯,此時再有交互,expect將不會進行交互,須要手動進行輸入內容交互。若是沒有這句,在須要交互的ssh命令執行完畢後將會退出遠程,而不是繼續保持在遠程。rest

expect參數

  • $argc:表示命令行參數個數
  • [lindex $argv n]:表示index爲n的參數(index從0開始計算),index的區間爲左閉右開,如[lindex $argv 0]表明命令行輸入的第一個參數,[lindex $argv 0 3] 表明命令行輸入的第一到第三個參數 。

示例

#!/usr/bin/expect

set host [lindex $argv 0]
set username [lindex $argv 1]
set num $argc
if { num < 3 } {
    ...
}
  • 將第一個命令行參數賦值給變量host,將第二個命令行參數賦值給變量username,將總參數個數賦值給變量num。

expect流程控制

if語句:

if {條件1} {
    expect {
        "(yes/no)"
        {
        send "yes\r"
        expect "*assword:" {send "$password\r"}
        }
        "password"
        {
        send "$password\r"
    }
} else {
    puts "Expect was timeout"
    send_user "Expect was timeout"
}

expect {}: 多行指望,從上往下匹配,匹配成功裏面的哪一條,將執行與之的send命令

puts與send_user: 打印信息,相似echo

其餘:

  • 判斷條件用{}包含
  • 花括號與花括號,和括號與控制語句之間須要有空格,不然會報錯expect:extra characters after close-brace
  • if右邊要有左花括號,else左邊要有右花括號,不能單獨一行

for語句:

for {set i 0} {$i < 10} {incr i} {
    puts "I inside first loop: $i"
}

while語句:

set i 0
while {$i < 10} {
    puts "I inside third loop: $i"
    incr i
    puts "I after incr: $i"
}

incr: 遞增運算符 incr i ,相似++

其餘的一些內容

  • 使用正則匹配:使用 -re選項,expect -re "\\\[(.*)]" 其中[在expect shell 正則中都有特殊意義,所以要\三次
  • exp_continue: 循環匹配,一般匹配以後會退出語句,但使用exp_continue 則能夠不斷循環執行某段語句

    expect {
        "password" {
            send "$password\r"
            exp_continue    # 不斷匹配字符串"password",只要匹配成功就send
        }
    }
    expect eof
  • shell 嵌套使用expect,使用重定向,須要注意EOF之間的互相對應

    #!/bin/bash
    hostname=$1                #接收第一個參數
    password=$2
    /usr/bin/expect <<-EOF    # 重定向到expect
    spawn ssh root@${hostname}
    expect {
            "(yes/no)"
            {
            send "yes\r"
            expect "*assword:" {send "$password\r"}
            }
            "password"
            {
            send "$password\r"
            }
    }
    expect eof
    EOF   # 因爲用的-EOF,這裏的EOF能夠有空格,tab鍵
  • excpet中執行shell語句,exec sh -c {shell語句},多用於賦值變量,須要注意的是,expect裏執行的shell語句,即便有打印和交互內容(echo,read命令)也不會輸出到終端,即執行了命令,你並不知道是否出錯,也不知道執行結果,若是須要將shell中echo命令打印的內容輸出到終端,只能將執行結果賦值給expect變量,再使用puts命令打印出來,但即便這樣,也會出現一些莫名頭疼的問題,因此儘可能不要在expect中調用複雜的shell語句。

    exec sh -c {shell 命令}    # 執行的shell命令即便有打印和須要交互的內容也不會出如今終端
    
    set test_echo [exec sh -c {echo "test"}]
    puts "$test_echo"
  • expect/shell互相使用彼此變量

    • 若是二者在同一文件中,二者只是做爲一段語句存在,使用#!/bin/bash解釋的shell文件,expect調用shell變量直接$變量,和shell腳本調用變量方式並沒有異同,使用#!/usr/bin/expect解釋的expect腳本文件,shell做爲expect文件的語句,如set a [exec sh -c {echo \$LAB}]調用expect變量,須要在expect裏面設置環境變量,如:set ::env(LAB) my_lab
    • 若是二者是分別爲不一樣文件,expect做爲腳本在shell腳本文件中被調用,如./test.excp,首先須要在shell中進行變量export, 例如export a="test", 而後在expect腳本文件中經過 $::env(a) 引用shell腳本文件的變量,例如set a_exp \$::env(a),同時也能夠經過執行子shell調用,例如:set a [exec sh -c {echo $a}]
  • 向進程發送Ctcl + c,若是想向遠端發送Ctrl-C結束遠端進程,能夠經過send "\003" 實現

    

參考博客:

果凍想-Linux expect詳解

taoyuanforrest-expect使用技巧

相關文章
相關標籤/搜索