1. 程式人生 > >通過expect工具實現自動化管理

通過expect工具實現自動化管理

一、概述

        我們通過Shell可以實現簡單的控制流功能,如:迴圈、判斷等。但是對於需要互動的場合則必須通過人工來干預,有時候我們可能會需要實現和互動程式如telnet伺服器等進行互動的功能。而expect就使用來實現這種功能的工具。

       expect是一個免費的程式設計工具語言,用來實現自動和互動式任務進行通訊,而無需人的干預。expect是不斷髮展的,隨著時間的流逝,其功能越來越強大,已經成為系統管理員的的一個強大助手。expect需要Tcl程式語言的支援,要在系統上執行expect必須首先安裝Tcl。
二、expect的安裝

expect是在Tcl基礎上建立起來的,所以在安裝expect前我們應該先安裝Tcl。
(一)Tcl 安裝

主頁: http://www.tcl.tk
下載地址: http://www.tcl.tk/software/tcltk/downloadnow84.tml
1.下載原始碼包
wget http://nchc.dl.sourceforge.net/sourceforge/tcl/tcl8.4.11-src.tar.gz  

2.解壓縮原始碼包
tar xfvz tcl8.4.11-src.tar.gz  

3.安裝配置

cd tcl8.4.11/unix  
./configure --prefix=/usr/tcl --enable-shared  
make  
make install  


注意:
1、安裝完畢以後,進入tcl原始碼的根目錄,把子目錄unix下面的tclUnixPort.h copy到子目錄generic中。

2、暫時不要刪除tcl原始碼,因為expect的安裝過程還需要用。
(二)expect 安裝 (需Tcl的庫)

主頁: http://expect.nist.gov/
1.下載原始碼包

wget http://sourceforge.net/projects/expect/files/Expect/5.45/expect5.45.tar.gz/download

2.解壓縮原始碼包

tar xzvf expect5.45.tar.gz  

3.安裝配置

cd expect5.45  
./configure --prefix=/usr/expect --with-tcl=/usr/tcl/lib --with-tclinclude=../tcl8.4.11/generic  
make  
make install  
ln -s /usr/tcl/bin/expect /usr/expect/bin/expect

或者直接用yum:
yum -y install expect


三、Expect工作原理

       從最簡單的層次來說,Expect的工作方式象一個通用化的Chat指令碼工具。Chat指令碼最早用於UUCP網路內,以用來實現計算機之間需要建立連線時進行特定的登入會話的自動化。

       Chat指令碼由一系列expect-send對組成:expect等待輸出中輸出特定的字元,通常是一個提示符,然後傳送特定的響應。例如下面的 Chat指令碼實現等待標準輸出出現Login:字串,然後傳送somebody作為使用者名稱;然後等待Password:提示符,併發出響應 sillyme。

引用:
Login: somebody Password: sillyme  


Expect最簡單的指令碼操作模式本質上和Chat指令碼工作模式是一樣的。

例子:
1、實現功能

下面我們分析一個響應chsh命令的指令碼。我們首先回顧一下這個互動命令的格式。

假設我們要為使用者chavez改變登入指令碼,要求實現的命令互動過程如下:


    # chsh chavez   
    Changing the login shell for chavez   
    Enter the new value, or press return for the default   
    Login Shell [/bin/bash]: /bin/tcsh   
    #  


可以看到該命令首先輸出若干行提示資訊並且提示輸入使用者新的登入shell。我們必須在提示資訊後面輸入使用者的登入shell或者直接回車不修改登入shell。

2、實現自動執行


#!/usr/bin/expect  
# Change a login shell to tcsh  
set user [lindex $argv 0]  
spawn chsh $user  
expect "]:"  
send "/bin/tcsh "   
expect eof  
 
exit  


說明:

(1)首行指定用來執行該指令碼的命令程式,這裡是/usr/bin/expect。

(2)程式第一行用來獲得指令碼的執行引數(其儲存在陣列$argv中,從0號開始是引數),並將其儲存到變數user中。

(3)第二個引數使用expect的spawn命令來啟動指令碼和命令的會話,這裡啟動的是chsh命令,實際上命令是以衍生子程序的方式來執行的。

(4)隨後的expect和send命令用來實現互動過程。指令碼首先等待輸出中出現]:字串,一旦在輸出中出現chsh輸出到的特徵字串(一般特徵 字串往往是等待輸入的最後的提示符的特徵資訊)。對於其他不匹配的資訊則會完全忽略。當指令碼得到特徵字串時,expect將傳送/bin/tcsh和 一個回車符給chsh命令。最後指令碼等待命令退出(chsh結束),一旦接收到標識子程序已經結束的eof字元,expect指令碼也就退出結束。
3、決定如何響應

       系統管理員往往有這樣的需求,希望根據當前的具體情況來以不同的方式對一個命令進行響應。我們可以通過後面的例子看到expect可以實現非常複雜的條件響應,而僅僅通過簡單的修改預處理指令碼就可以實現。

     下面的例子是一個更復雜的expect-send例子:

expect -re "\[(.*)]:"  
if {$expect_out(1,string)!="/bin/tcsh"} {  
send "/bin/tcsh" }  
send " "  
expect eof


說明:

(1)第一個expect命令現在使用了-re引數,這個引數表示指定的的字串是一個正則表示式,而不是一個普通的字串。對於上面這個例子裡是查詢一個左方括號字元(其必須進行三次逃逸(escape),因此有三個符號,因為它對於expect和正則表達時來說都是特殊字元)後面跟有零個或多個字元,最後是一個右方括號字元。這裡.*表示表示一個或多個任意字元,將其存放在()中是因為將匹配結果存放在一個變數中以實現隨後的對匹配結果的訪問。

(2)當發現一個匹配則檢查包含在[]中的字串,檢視是否為/bin/tcsh。如果不是則傳送/bin/tcsh給chsh命令作為輸入,如果是則僅僅傳送一個回車符。這個簡單的針對具體情況發出不同相響應的小例子說明了expect的強大功能。

(3)在一個正則表達時中,可以在()中包含若干個部分並通過expect_out陣列訪問它們。各個部分在表示式中從左到右進行編碼,從1開始(0包含有整個匹配輸出)。()可能會出現巢狀情況,這這種情況下編碼從最內層到最外層來進行的。
4、使用超時

       下一個expect例子中將闡述具有超時功能的提示符函式。這個指令碼提示使用者輸入,如果在給定的時間內沒有輸入,則會超時並返回一個預設的響應。這個指令碼接收三個引數:提示符字串,預設響應和超時時間(秒)。

#!/usr/bin/expect  
# Prompt function with timeout and default.  
 
#指令碼的第一部分首先是得到執行引數並將其儲存到內部變數中  
set prompt [lindex $argv 0]  
set def [lindex $argv 1]   
set response $def  
set tout [lindex $argv 2]   
 
send_tty "$prompt: "  
#send_tty命令用來實現在終端上顯示提示符字串和一個冒號及空格  
set timeout $tout  
#set timeout命令設定後面所有的expect命令的等待響應的超時時間為$tout(-l引數用來關閉任何超時設定)。   
expect " " {  
set raw $expect_out(buffer)  
 
# remove final carriage return  
set response [string trimright "$raw" " "]  
}  
if {"$response" == "} {set response $def}  
send "$response "  
 
# Prompt function with timeout and default.  
set prompt [lindex $argv 0]  
set def [lindex $argv 1]   
set response $def  
set tout [lindex $argv 2]

說明:

(1)send_tty命令用來實現在終端上顯示提示符字串和一個冒號及空格。

(2)set timeout命令設定後面所有的expect命令的等待響應的超時時間為$tout(-l引數用來關閉任何超時設定)。

(3)然後expect命令就等待輸出中出現回車字元。如果在超時之前得到回車符,那麼set命令就會將使用者輸入的內容賦值給變臉raw。隨後的命令將使用者輸入內容最後的回車符號去除以後賦值給變數response。

(4)如果response中內容為空則將response值置為預設值(如果使用者在超時以後沒有輸入或者使用者僅僅輸入了回車符)。最後send命令將response變數的值加上回車符傳送給標準輸出。

注意:

(1)該指令碼沒有使用spawn命令。

(2)該expect指令碼會與任何呼叫該指令碼的程序互動。

(3)如果該指令碼名為prompt,那麼它可以用在任何C風格的shell中。

% set a='prompt "Enter an answer" silence 10'   
Enter an answer: test   
 
% echo Answer was "$a"   
Answer was test

prompt設定的超時為10秒。如果超時或者使用者僅僅輸入了回車符號,echo命令將輸出
Answer was "silence"   


5、一個更復雜的例子

       下面我們將討論一個更加複雜的expect指令碼例子,這個指令碼使用了一些更復雜的控制結構和很多複雜的互動過程。這個例子用來實現傳送write命令給任意的使用者,傳送的訊息來自於一個檔案或者來自於鍵盤輸入。


    #!/usr/bin/expect  
    # Write to multiple users from a prepared file  
    # or a message input interactively  
      
    if {$argc<2} {  
    send_user "usage: $argv0 file user1 user2 ... "  
    exit  
    }   
    #send_user命令用來顯示使用幫助資訊到父程序(一般為使用者的shell)的標準輸出。   
      
    set nofile 0  
      
    # get filename via the Tcl lindex function  
    set file [lindex $argv 0]  
    if {$file=="i"} {   
    set nofile 1   
    } else {   
      
    # make sure message file exists  
    if {[file isfile $file]!=1} {   
    send_user "$argv0: file $file not found. "  
    exit }}   
      
    ####################################################  
    #(1)這部分實現處理指令碼啟動引數,其必須是一個儲存要傳送的訊息的檔名或表示使用互動輸入得到傳送消的內容的"i"命令。   
    #(2)變數file被設定為指令碼的第一個引數的值,是通過一個Tcl函式lindex來實現的,該函式從列表/陣列得到一個特定的元素。[]用來實現將函式lindex的返回值作為set命令的引數。   
    #(3)如果指令碼的第一個引數是小寫的"i",那麼變數nofile被設定為1,否則通過呼叫Tcl的函式isfile來驗證引數指定的檔案存在,如果不存在就報錯退出。   
    #(4)可以看到這裡使用了if命令來實現邏輯判斷功能。該命令後面直接跟判斷條件,並且執行在判斷條件後的{}內的命令。if條件為false時則執行else後的程式塊。   
    #######################################################  
      
    set procs {}  
    # start write processes  
      
    for {set i 1} {$i<$argc}  
    {incr i} {  
    spawn -noecho write   
    [lindex $argv $i]   
    lappend procs $spawn_id  
    }   
    #######################################################################################  
    #(1)這一部分使用spawn命令來啟動write程序實現向用戶傳送訊息.  
    #(2)這裡使用了for命令來實現迴圈控制功能,迴圈變數首先設定為1,然後因此遞增。迴圈體是最後的{}的內容。  
    #(3)這裡我們是用指令碼的第二個和隨後的引數來spawn一個write命令,並將每個引數作為傳送訊息的使用者名稱。  
    #(4)lappend命令使用儲存每個spawn的程序的程序ID號的內部變數$spawn_id在變數procs中構造了一個程序ID號列表。  
    ###################################################################################################  
      
    if {$nofile==0} {  
    setmesg [open "$file" "r"]  
    } else {  
    send_user "enter message,  
    ending with ^D: " }   
    #最後指令碼根據變數nofile的值實現開啟訊息檔案或者提示使用者輸入要傳送的訊息。   
      
    set timeout -1  
    while 1 {  
    if {$nofile==0} {  
    if {[gets $mesg chars] == -1} break  
    set line "$chars "   
    } else {  
    expect_user {  
    -re " " {}  
    eof break }  
    set line $expect_out(buffer) }  
      
    foreach spawn_id $procs {   
    send $line }  
    sleep 1}  
    exit   
    ########################################################  
    #(1)這段程式碼說明了實際的訊息文字是如何通過無限迴圈while被髮送的。  
    #(2)while迴圈中的if判斷訊息是如何得到的。在非互動模式下,下一行內容從訊息檔案中讀出,當檔案內容結束時while迴圈也就結束了。(break命令實現終止迴圈) 。   
    #(3)在互動模式下,expect_user命令從使用者接收訊息,當用戶輸入ctrl+D時結束輸入,迴圈同時結束。 兩種情況下變數$line都被用來儲存下一行訊息內容。當是訊息檔案時,回車會被附加到訊息的尾部。   
    #(4)foreach迴圈遍歷spawn的所有程序,這些程序的ID號都儲存在列表變數$procs中,實現分別和各個程序通訊。send命令組成了foreach的迴圈體,傳送一行訊息到當前的write程序。while迴圈的最後是一個sleep命令,主要是用於處理非互動模式情況下,以確保訊息 不會太快的傳送給各個write程序。當while迴圈退出時,expect指令碼結束。   
    ########################################################  



四、使用expect指令碼的小竅門
1、使用“-c”選項,從命令列執行expect指令碼
expect可以讓你使用“-c”選項,直接在命令列中執行它,如下所示:
    $ expect -c 'expect "\n" {send "pressed enter\n"}       
    pressed enter  
    $  


如果你執行了上面的指令碼,它會等待輸入換行符(\n)。按“enter”鍵以後,它會打印出“pressed enter”這個訊息,然後退出。
2、使用“-i”選項互動地執行expect指令碼

使用“-i”選項,可以通過來自於標準輸入的讀命令來互動地執行expect指令碼。如下所示:



$ expect -i arg1 arg2 arg3  
expect1.1>set argv  
arg1 arg2 arg3  
expect1.2>


正常情況下,當你執行上面的expect命令的時候(沒有“-i”選項),它會把arg1當成指令碼的檔名,所以“-i”選項可以讓指令碼把多個引數當成一個連續的列表。

當你執行帶有“-c”選項的expect指令碼的時候,這個選項是十分有用的。因為預設情況下,expect是互動地執行的。
3、當執行expect指令碼的時候,輸出除錯資訊

當你用“-d”選項執行程式碼的時候,你可以輸出診斷的資訊。如下所示:



    $ cat sample.exp  
    # !/usr/bin/expect -fexpect "\n";send "pressed enter";$ expect -d sample.expexpect version 5.43.0argv[0] = expect  argv[1] = -d  argv[2] = sample.expset argc 0set argv0 "sample.exp"set argv ""executing commands from command file sample.exp  
      
    expect: does "" (spawn_id exp0) match glob pattern "\n"? no  
      
    expect: does "\n" (spawn_id exp0) match glob pattern "\n"? yes  
    expect: set expect_out(0,string) "\n"  
    expect: set expect_out(spawn_id) "exp0"  
    expect: set expect_out(buffer) "\n"  
    send: sending "pressed enter" to { exp0 pressed enter}  


4、使用“-D”選項啟動expect偵錯程式

“-D”選項用於啟動偵錯程式,它只接受一個布林值的引數。這個引數表示提示器必須馬上啟動,還是隻是初始化偵錯程式,以後再使用它。

$ expect -D 1 script  

“-D”選項左邊的選項會在偵錯程式啟動以前被處理。然後,在偵錯程式啟動以後,剩下的命令才會被執行。

$ expect -c 'set timeout 10' -D 1 -c 'set a 1'  
1: set a 1  
dbg1.0>



5、逐行地執行expect指令碼

通常,expect會在執行指令碼之前,把整個指令碼都讀入到記憶體中。“-b”選項可以讓expect一次只讀取指令碼中的一行。當你沒有寫完整個指令碼的時候,這是十分有用的,expect可以開始執行這個不完整的指令碼,並且,它可以避免把指令碼寫入到臨時檔案中。
$ expect -b  

 

6、讓expect不解釋命令列引數

你可以使用識別符號讓expect不解釋命令列引數。

你可以像下面這樣的讀入命令列引數:



    $ cat  print_cmdline_args.exp  
    #!/usr/bin/expect  
    puts 'argv0 : [lindex $argv 0]';  
    puts 'argv1 : [lindex $argv 1]';  


當執行上面的指令碼的時候,會跳過命令列選項,它們會被當成引數(而不是expect選項),如下所示:

$ expect print_cmdline_args.exp -d -c  
argv0 : -d  
argv1 : -c


四、expect簡單例子

為了更好理解except指令碼幾個簡單引數,我們再舉一個簡單的例子:


    #!/usr/bin/expect     
    set timeout 30     
    spawn ssh -l username 192.168.1.1     
    expect "password:"     
    send "ispass\r"     
    interact    



說明:   
1. [#!/usr/bin/expect]   
    這一行告訴作業系統腳本里的程式碼使用那一個shell來執行。這裡的expect其實和linux下的bash、windows下的cmd是一類東西。   
注意:這一行需要在指令碼的第一行。   
   
2. [set timeout 30]     
    基本上認識英文的都知道這是設定超時時間的,現在你只要記住他的計時單位是:秒   
    
3. [spawn ssh -l username 192.168.1.1]    
    spawn是進入expect環境後才可以執行的expect內部命令,如果沒有裝expect或者直接在預設的SHELL下執行是找不到spawn命令的。所以不要用 “which spawn“之類的命令去找spawn命令。好比windows裡的dir就是一個內部命令,這個命令由shell自帶,你無法找到一個dir.com 或 dir.exe 的可執行檔案。    
    它主要的功能是給ssh執行程序加個殼,用來傳遞互動指令。   
   
4. [expect "password:"]   
    這裡的expect也是expect的一個內部命令,有點暈吧,expect的shell命令和內部命令是一樣的,但不是一個功能,習慣就好了。這個命令的意思是判斷上次輸出結果裡是否包含“password:”的字串,如果有則立即返回,否則就等待一段時間後返回,這裡等待時長就是前面設定的30秒   
   
5. [send "ispass\r"]   
    這裡就是執行互動動作,與手工輸入密碼的動作等效。   
    溫馨提示: 命令字串結尾別忘記加上 “\r”,如果出現異常等待的狀態可以核查一下。   
   
6. [interact]    
    執行完成後保持互動狀態,把控制權交給控制檯,這個時候就可以手工操作了。如果沒有這一句登入完成後會退出,而不是留在遠端終端上。如果你只是登入過去執行一段命令就退出,可改為[expect eof]  

五、expect實用案例
1、expect實現ssh無金鑰登陸

說明:用了兩個指令碼,一個bash指令碼(send_key.sh),在其中呼叫另外一個expect指令碼(scp_key_to_node.exp),兩個指令碼放在同一個目錄下:
(1)bash指令碼:send_key.sh
#!/bin/bash    
ssh-keygen -t dsa    
for (( i = 1; i <= 100 ; i ++ ))    
do    
  ./scp_key_to_node.exp $i    
done  


(2)expect指令碼:(scp_key_to_node.exp)

    #!/usr/bin/expect    
    set timeout 5    
    set hostno [lindex $argv 0]    
    spawn scp ~/.ssh/id_dsa.pub impala$hostno:~/.ssh/pub_key    
    expect "*password*"    
    send "111111\r"    
    spawn ssh impala$hostno "cat ~/.ssh/pub_key/ >> ~/.ssh/authorized_keys"    
    expect "*password*"    
    send "111111\r"    
    spawn ssh impala$hostno "chmod 600 ~/.ssh/authorized_keys"    
    expect "*password*"    
    send "111111\r"    
    expect eof    


(3)分析:
set可以設定超時,或者設定一個變數的值
spawn是執行一個命令
expect等待一個匹配的輸出流中的內容
send是匹配到之後向輸入流寫入的內容
[lindex $argv 0]表示指令碼的第0個引數
expect eof表示讀取到檔案結束符
(4)指令碼執行方式:
在指令碼所在的目錄下執行:
# ./send_key.sh


2、ssh實現自動登入,並停在登入伺服器上

    #!/usr/bin/expect -f  
     set ip [lindex $argv 0 ]     //接收第一個引數,並設定IP  
     set password [lindex $argv 1 ]   //接收第二個引數,並設定密碼  
     set timeout 10                   //設定超時時間  
     spawn ssh
[email protected]
$ip       //傳送ssh請滶  
     expect {                 //返回資訊匹配  
     "*yes/no" { send "yes\r"; exp_continue}  //第一次ssh連線會提示yes/no,繼續  
     "*password:" { send "$password\r" }      //出現密碼提示,傳送密碼  
     }  
     interact          //互動模式,使用者會停留在遠端伺服器上面.  


執行結果如下:
[email protected]:/home/zhangy# ./test.exp 192.168.1.130 admin  
spawn ssh
[email protected]
 
Last login: Fri Sep  7 10:47:43 2012 from 192.168.1.142  


3、根據IP和密碼連線到不同的機器.


    #!/usr/bin/expect -f  
      
     set ip 192.168.1.130  
     set password admin  
     set timeout 10  
     spawn ssh [email protected]$ip  
     expect {  
     "*yes/no" { send "yes\r"; exp_continue}  
     "*password:" { send "$password\r" }  
     }  


執行結果如下:

    [email protected]:/home/zhangy# ./web.exp  
    spawn ssh [email protected]  
    Last login: Fri Sep  7 12:59:02 2012 from 192.168.1.142  

4、遠端登入到伺服器,並且執行命令,執行完後並退出
#!/usr/bin/expect -f  
 set ip 192.168.1.130  
 set password admin  
 set timeout 10  
 spawn ssh [email protected]$ip  
 expect {  
 "*yes/no" { send "yes\r"; exp_continue}  
 "*password:" { send "$password\r" }  
 }  
 expect "#*"  
 send "pwd\r"  
 send  "exit\r"  
 expect eof

執行結果如下:
[email protected]:/home/zhangy# ./test3.exp  
spawn ssh [email protected]  
[email protected]'s password:  
Last login: Fri Sep  7 14:05:07 2012 from 116.246.27.90  
[[email protected] ~]# pwd  
/root  
[[email protected] ~]# exit  
logout  
Connection to 192.168.1.130 closed


5、遠端登入到ftp,並且下載檔案


    #!/usr/bin/expect -f  
     set ip [lindex $argv 0 ]  
     set dir [lindex $argv 1 ]  
     set file [lindex $argv 2 ]  
     set timeout 10  
     spawn ftp $ip  
     expect "Name*"  
     send "zwh\r"  
     expect "Password:*"  
     send "zwh\r"  
     expect "ftp>*"  
     send "lcd $dir\r"  
     expect {  
     "*file"  { send_user "local $_dir No such file or directory";send "quit\r" }  
     "*now*"  { send "get $dir/$file $dir/$file\r"}  
     }  
     expect {  
     "*Failed" { send_user "remote $file No such file";send "quit\r" }  
     "*OK"     { send_user "$file has been download\r";send "quit\r"}  
     }  
     expect eof  

執行結果如下:

    [email protected]:/home/zhangy# ./test2.exp 192.168.1.130 /var/www/www aaa.html  
    spawn ftp 192.168.1.130  
    Connected to 192.168.1.130.  
    220 (vsFTPd 2.0.5)  
    Name (192.168.1.130:root): zwh  
    331 Please specify the password.  
    Password:  
    230 Login successful.  
    Remote system type is UNIX.  
    Using binary mode to transfer files.  
    ftp> lcd /var/www/www  
    Local directory now /var/www/www  
    ftp> get /var/www/www/aaa.html /var/www/www/aaa.html  
    local: /var/www/www/aaa.html remote: /var/www/www/aaa.html  
    200 PORT command successful. Consider using PASV.  
    150 Opening BINARY mode data connection for /var/www/www/aaa.html (66 bytes).  
    226 File send OK.  
    66 bytes received in 0.00 secs (515.6 kB/s)  
    quit aaa.html has been download  
    221 Goodbye.  

6、使用expect呼叫passwd自動更改密碼

    #!/bin/bash  
    USER=mynameuser  
    PASS=oldpassword  
    NPASS=newpassword  
    expect << EOF  
    spawn passwd  
    expect "Changing password for ${USER}."  
    send "${PASS}\r"  
    expect "Enter new UNIX password:"  
    send "${NPASS}\r"  
    expect "Retype new UNIX password:"  
    send "${NPASS}\r"  
    expect eof;  
    EOF  

7、完成對伺服器的scp任務:


    #!/usr/bin/expect  
    set timeout 10  
    set host [lindex $argv 0]  
    set username [lindex $argv 1]  
    set password [lindex $argv 2]  
    set src_file [lindex $argv 3]  
    set dest_file [lindex $argv 4]  
    spawn scp $src_file [email protected]$host:$dest_file  
     expect {  
     "(yes/no)?"  
       {  
        send "yes\n"  
        expect "*assword:" { send "$password\n"}  
     }  
     "*assword:"  
    {  
     send "$password\n"  
    }  
    }  
    expect "100%"  
    expect eof  

說明:

(1)注意程式碼剛開始的第一行,指定了expect的路徑,與shell指令碼相同,這一句指定了程式在執行時到哪裡去尋找相應的啟動程式。程式碼剛開始還設定了timeout的時間為10秒,如果在執行scp任務時遇到了程式碼中沒有指定的異常,則在等待10秒後該指令碼的執行會自動終止。

(2)這個指令碼設定了5個需要手動輸入的引數,分別為:目標主機的IP、使用者名稱、密碼、本地檔案路徑、目標主機中的檔案路徑。如果將以上指令碼儲存為expect_scp檔案,則在shell下執行時需要按以下的規範來輸入命令:


./expect_scp 192.168.75.130 root 123456 /root/src_file /root/dest_file  


以上的命令執行後,將把本地/root目錄下的src_file檔案拷貝到使用者名稱為root,密碼為123456的主機192.168.75.130中的/root下,同時還將這個原始檔重新命名為dest_file。

(3)spawn代表在本地終端執行的語句,在該語句開始執行後,expect開始捕獲終端的輸出資訊,然後做出對應的操作。expect程式碼中的捕獲的(yes/no)內容用於完成第一次訪問目標主機時儲存金鑰的操作。有了這一句,scp的任務減少了中斷的情況。程式碼結尾的expect eof與spawn對應,表示捕獲終端輸出資訊的終止。

 

如果需要實現批量scp的任務,則需要再寫一個shell指令碼來呼叫這個expect指令碼。
    #!/bin/sh  
    list_file=$1  
    src_file=$2  
    dest_file=$3  
    cat $list_file | while read line  
    do  
       host_ip=`echo $line | awk '{print $1}'`  
       username=`echo $line | awk '{print $2}'`  
       password=`echo $line | awk '{print $3}'`  
       echo "$host_ip"  
       ./expect_scp $host_ip $username $password $src_file $dest_file  
    done   


指定了3個引數:列表檔案的位置、本地原始檔路徑、遠端主機目標檔案路徑。需要說明的是其中的列表檔案指定了遠端主機ip、使用者名稱、密碼,這些資訊需要寫成以下的格式:
IP username password

中間用空格或tab鍵來分隔,多臺主機的資訊需要寫多行內容,如:
192.168.75.130 root 123456
192.168.75.131 knktc testpass

這樣就指定了兩臺遠端主機的資訊。注意,如果遠端主機密碼中有“$”、“#”這類特殊字元的話,在編寫列表檔案時就需要在這些特殊字元前加上轉義字元,否則expect在執行時會輸入錯誤的密碼。

執行指令碼:
./batch_scp.sh ./hosts.list /root/src_file /root/destfile  




用這兩個指令碼檔案,就可以簡單地完成批量scp的任務了。
六、綜合例子
1、自動化指令碼建立主機之間的SSH信任關係


    #!/usr/bin/ksh  
    #usage ./ssh_trust.sh host1 user1 passwd1 host2 user2 passwd2  
    #即建立從[email protected][email protected]的ssh信任。  
    src_host=$1  
    src_username=$2  
    src_passwd=$3  
      
    dst_host=$4  
    dst_username=$5  
    dst_passwd=$6  
      
    #在遠端主機1上生成公私鑰對  
    Keygen()  
    {  
    expect << EOF  
    spawn ssh [email protected]$src_host ssh-keygen -t rsa  
    while 1 {  
            expect {  
                     "password:" {  
                                  send "$src_passwd\n"  
                                   }  
                    "yes/no*" {  
                                send "yes\n"  
                              }  
                            "Enter file in which to save the key*" {  
                                            send "\n"  
                            }  
                            "Enter passphrase*" {  
                                            send "\n"  
                            }  
                            "Enter same passphrase again:" {  
                                            send "\n"  
                                            }  
      
                            "Overwrite (y/n)" {  
                                            send "n\n"  
                            }  
                            eof {  
                                       exit  
                            }  
            }  
    }  
    EOF  
    }  
    #從遠端主機1獲取公鑰儲存到本地  
    Get_pub()  
    {  
    expect << EOF  
    spawn scp [email protected]$src_host:~/.ssh/id_rsa.pub /tmp  
    expect {  
                 "password:" {  
                               send "$src_passwd\n";exp_continue  
                    }  
                    "yes/no*" {  
                               send "yes\n";exp_continue  
                    }     
                    eof {  
                                    exit  
                    }  
    }  
    EOF  
    }  
    #將公鑰的內容附加到遠端主機2的authorized_keys  
    Put_pub()  
    {  
    src_pub="$(cat /tmp/id_rsa.pub)"  
    expect << EOF  
    spawn ssh [email protected]$dst_host "mkdir -p ~/.ssh;echo $src_pub >> ~/.ssh/authorized_keys;chmod 600 ~/.ssh/authorized_keys"  
    expect {  
                "password:" {  
                            send "$dst_passwd\n";exp_continue  
                 }  
                "yes/no*" {  
                            send "yes\n";exp_continue  
                 }     
                eof {  
                            exit  
                 }   
    }  
    EOF  
    }  
    Keygen  
    Get_pub  
    Put_pub  















通過expect工具去批量管理多臺主機,不管是查詢或者執行命令都可以通過此方法去批量管理(自己生產環境使用的案例,可直接拿去使用)

==========配置server端===================
a. 配置expect檔案  Day.sh:
#!/usr/bin/expect -f

set timeout 30

set pwd1 abc123
set pwd2 qwe123
set standard [lindex $argv 0]
set fd [open "/home/yanyb/DayIp.txt" r]
set number 0

while { [gets $fd data] >= 0 } {
set ip [exec echo $data | awk "{ print \$1 }"]
set ts1 [exec echo $data | awk "{ print \$2 }"]
set ts2 [exec echo $data | awk "{ print \$3 }"]
set ts3 [exec echo $data | awk "{ print \$4 }"]
set ts4 [exec echo $data | awk "{ print \$5 }"]
set ts5 [exec echo $data | awk "{ print \$6 }"]
set ts6 [exec echo $data | awk "{ print \$7 }"]
spawn ssh [email protected]$ip
expect {
    "(yes/no)?" {
        send "yes\n"
        expect "assword:"
        send "$pwd1\n"
    }
        "assword:" {
        send "$pwd1\n"
    }
 }
 expect "$ts1"
 send "su -\r"
 expect "assword:"
 send "$pwd2\r"
 expect "$ts2"
 send "cd /home/yanyb\r"
 expect "$ts3"
 send "./start.sh $standard &\r"
 expect "$ts4"
 send "exit\r"
 expect "$ts5"
 send "exit\r"
 expect "$ts6"
 sleep 2
 }
 close $fd
 


b. 配置DayIp.txt(此檔案是存放ip地址的):

10.132.22.11 # # # # $ ]
10.132.22.21 # # # # $ ]
10.132.23.11 $ # # # $ ]
10.132.23.21 $ # # # $ ]
10.132.24.11 # $ $ $ # ]
10.132.24.21 # # # # # ]
10.132.25.11 # # # # # ]
10.132.25.21 # # # # # ]
10.132.28.11 ] ] ] ] ] ]
10.132.28.21 ] ] ] ] ] ]


 
c. Daysehll檔案存放要執行的命令:
 
echo -e "\n****************************vmstat:記憶體及CPU使用率情況****************************"
echo "[`hostname`][/]# vmstat 1 10"
vmstat 1 10
echo "****************************vmstat:記憶體及CPU使用率情況****************************"


echo -e "\n*******************************iostat:IO使用率情況********************************"   
echo "[`hostname`][/]# iostat"
iostat
echo "*******************************iostat:IO使用率情況********************************"  

 
 
d. day.log檔案是輸出的日誌:
 略


============配置client端===================

#!/bin/bash
#####
#從serv傳遞了一個標準檔案$1 standardfile

standard=$1
suffix=`hostname`
#FTP及路徑配置資訊
FtpUser=yanyb  #注意:這個使用者一定要能正常登入server端並上傳下載東西
FtpPass=abc123
FtpAddress=這裡指server端的IP地址

#下載指令碼
/usr/bin/ftp -i -n -v <<-!
open $FtpAddress
user $FtpUser $FtpPass
bin
prompt off
cd /home/yanyb #yanyb使用者要有許可權,否則上傳下載不成功
get ${standard} ${standard}.sh
bye
!

chmod +x ${standard}.sh
./${standard}.sh>${suffix}.log

#上傳結果檔案
rm ${standard}.sh
/usr/bin/ftp -i -n -v <<-!
open $FtpAddress
user $FtpUser $FtpPass
bin
prompt off
cd /home/yanyb/log #同樣yanyb使用者要有許可權,否則上傳下載不成功
put ${suffix}.log
bye
!


執行命令:

nohup ./Day.sh Daysehll >day.log 2>&1 &




說明:

Day.sh

1、檔案是expect去互動(注意:Day.sh檔案裡面還去DayIp.txt檔案裡面獲取ip);

2、Daysehll檔案將你想要在client端執行的命令。

3、day.log是將正確或者錯誤的結果輸入到該檔案。


相關推薦

通過expect工具實現自動化管理

一、概述        我們通過Shell可以實現簡單的控制流功能,如:迴圈、判斷等。但是對於需要互動的場合則必須通過人工來干預,有時候我們可能會需要實現和互動程式如telnet伺服器等進行互動的功能。而expect就使用來實現這種功能的工具。       expect是一個

Linux - 通過expect工具實現指令碼的自動互動

目錄 1 安裝expect工具 2 expect的常用命令 3 作用原理簡介 3.1 示例指令碼 3.2 指令碼功能解讀 4 其他指令碼使用示例

iOS開發之使用fastlane工具實現自動化打包發布

TP ruby success 2.0 提交 gui ava 新的 unit test p.p1 { margin: 0.0px 0.0px 0.0px 0.0px; font: 14.0px "PingFang SC"; color: #000000; backgroun

通過WinSCP工具實現Windows服務與Linux伺服器之間的檔案傳輸

2、WinSCP登入 解壓之後雙擊WinSCP.exe開啟 輸入Linux伺服器ip、埠、使用者名稱密碼進行連線 為便於下次使用,輸入完Linux伺服器資訊之後點選儲存 下次登入直接選中‘站點名稱’點選登入即可 3、WinSCP檔案傳輸 選中檔案進行

【雲星資料---Nik(精品版)】:通過ansible playbook實現自動化部署 KUBERNETES 叢集

環境要求 實驗環境通過MBP下的virtualbox和vagrant來提供3臺虛擬機器(centos 7)進行部署,其中1臺配置為k8s master其他2臺配置為k8s nodes。可以根據實際環境擴充套件k8s noded的數量。 假設虛擬機器I

maven版本控制實現自動化管理JAR

解決maven deploy可生成版本號帶最新時間戳的問題 解決專案自動化管理(reimport)JAR下載私服上最新版本的JAR的問題 /** *@Author: ludezh *@

通過logmnr工具實現資料恢復

最近接到的case.接到客戶報障,xxx資料庫的一個關鍵表資料被誤刪除了,需要做緊急資料恢復。接到客戶報障後,隨後聯絡了開發商的工程師。 一.問題分析,經過與局方和開發商的討論,以及根據系統情況,瞭解到如下資訊。 1.誤刪除的表名為EMP使用者下的EPM_PRO_WORK

通過sysbench工具實現MySQL資料庫的效能測試

1.背景  sysbench是一款壓力測試工具,可以測試系統的硬體效能,也可以用來對資料庫進行基準測試。sysbench 支援的測試有CPU運算效能測試、記憶體分配及傳輸速度測試、磁碟IO效能測試、POSIX執行緒效能測試、互斥性測試測試、資料庫效能測試(OLTP基準測試)。目前支援的資料庫主要是

Linux下通過crontab及expect實現自動化處理

目標 為實現每天定時從其他伺服器上覆制檔案到本地,需要使用crontab建立定時任務,並通過scp進行Linux之間的檔案複製。在複製檔案時,可能需要輸入目標伺服器上的密碼,通過expect即可實現。 步驟說明 準備工作 檢查並安裝expect及

把編譯安裝的httpd 實現服務腳本,通過service和chkconfig 進行管理

成功 服務腳本 文件內容 roc grep sharp list roo httpd 把編譯安裝的httpd 實現服務腳本,通過service和chkconfig 進行管理 1 編譯安裝httpd 把httpd編譯安裝在/app/httpd/目錄下。 2 在/e

expect的用法和實現自動化腳本

linux expectexpect 簡介expect 是用來進行自動化控制和測試的工具。 expect 基本使用在linux運維和開發中,我們經常需要遠程登錄服務器進行操作,登錄的過程是一個交互的過程,可能會需要輸入yes/no password等信息。為了模擬這種輸入,可以使用expect腳本。expe

使用cobbler工具實現centos 6,7系統的自動化安裝

定義 name -name 等等 足夠 圖片 yum源 主機 配置yum源 vmware裏面準備兩臺虛擬機,一臺用於安裝cobbler服務器,另一臺當作測試機使用,cobbler服務器需要兩塊網卡,一塊需要連接外網,需要使用epel源。測試機使用一塊僅主機的模式的網卡,註意

如何通過配置IPMI實現遠程管理服務器

smbus 報錯 控制臺 admin 如果 但是 mic respond src 遠程登陸到設備配置IPMIipmitool 的安裝首先檢查設備是否支持dmidecode | grep -i ipmi如果看到 "IPMI DEVICE INFORMATION&qu

關於 MongoDB 與 SQL Server 通過本身自帶工具實現數據快速遷移 及 註意事項 的探究

數據遷移工具 文件中 文件導入 原本 修改字段 信息 字符 變化 是否 背景介紹 隨著業務的發展、需求的變化,促使我們追求使用不同類型的數據庫,充分發揮其各自特性。如果決定采用新類型的數據庫,就需要將既有的數據遷移到新的數據庫中。在這類需求中,將SQL Server中的數據

詳解oracle 12c通過數據泵expdp/impdp工具實現對數據備份、恢復

工具實現 數據庫 RoCE app eat source .com blog con 簡介 Oracle Database 10g引入了最新的數據泵(Data Dump)技術,數據泵導出導入(EXPDP和IMPDP)的作用1.實現邏輯備份和邏輯恢復2.數據庫用戶之間移動對象

Ansible批量自動化管理工具入門

這樣的 ase 更新 系統版本 roo art 啟動服務 移除 nag 一、虛擬機版本 1、需要利用7.5版本虛擬機 2、7.5版註意事項: 【2.1】、網卡名叫ens32同樣配置文件也是ens32 【2.2】、命令:systemctl 統一管理命令, 例,systemct

oa辦公自動化如何實現專案管理

企業辦公中,有大量事項需要跨部門協作,並且需要較長一段時間才能完成,此類事項多采用專案管理的 方式進行,因專案的週期長、涉及到的人員多、而且所涉及到的事項和管理內容繁雜,故而工作效率一般 不高。藉助資訊化的方式,工作效率當會有很大提升。這就需要oa辦公自動化專案管理子系統。 1、在專案許可權控制上 oa

自動化運維專題(二):Ansible批量自動化管理工具

一,工具簡介   1.1 ansible簡介   批量管理伺服器工具   無需部署agent,通過ssh進行管理   中小型公司常用的自動化運維工具     1.2 jenkins簡介   視覺化運維(主要用在視覺化部署)   持續構建,可以和git,snv結合   可結合ssh實

教你實現專案管理自動化(二)

我們在上一次的分享中,講解了Maven的環境變數配置及在IDEA中的整合,今天呢,就來給大家分享一下Maven的使用.Maven有兩種使用方式,第一種呢,比較原始,就是直接在cmd的命令列視窗中去使用,第二種呢,就是在IDE(Eclipse或IDEA都行)中去使用.一般來說,我們都會在IDE裡去使用。我們開啟

DEVOPS 運維開發系列五:基於Django過濾器實現自動化運維平臺功能模組的動態授權管理與展示

1、關於Django過濾器 Django中提供了很多內建的過濾器和標籤,我們常用的例如下面這些: block(模板繼承) extends(模板繼承) filter(過濾器) for(迴圈) if(判斷) include(載入模板) 還有很多詳見官網