我們經常會遇到一些需要與服務器程序打交道的場景,比如,從登陸某個服務器,然后進行某項工作。這很平常,但是如果把這個工作自動化進行,你就需要一個程序能自動做你要告訴機器的事情,這樣,我們的expect就能大顯身手了。
首先,expect是一個簡單的工具語言,如要工作就是進行自動化的人機交互。它的作者對Expect的定義:是一個實現自動交互功能的軟件套件(a software suite for automating interactive tools),使用expect,它能幫助我們在合適的情景下進行合適的交互。
例子:我們要在凌晨登陸到一個linux服務器:192.168.1.1,然后執行一個命令做/home/test/a.sh,我們的思路是:寫一個expect腳本,然后用crontab在凌晨運行。
#!/usr/bin/expect -f
set timeout 30
spawn ssh -l test 192.168.1.1
expect "password:"
send "mypassword\r"
expect "~$*"
send "/home/test/a.sh\r"
send "exit\n"
expect eof
exit
expect 的核心功能,對於設定好的特定匹配形式,以相匹配的動作以應對。每一個expect后所跟的字符串(或者正則表達式)就是腳本所等待的匹配模式,每一個send 所做的工作就是對於各種的模式串,實施相應的動作。
第一行設定了腳本執行的程序,-f選項指的是expect執行一個文件
第二行,設定了本腳本所有的超時時間,單位是秒(s),如果超時,腳本將繼續向下進行(比如在等待某個模式出現,超時以后,會進行下一語句)。
第三行,expect使用spawn命令來啟動腳本和命令會話,這里啟動的是ssh命令,這里的ssh命令將會以子進程的方式產生。
下面就是交互的過程:ssh -l 登陸以后,會給要求客戶寫入密碼,所以等待出現“password:”,出現password:以后,需要寫入密碼,注意這里需要送去回車或者換行符,否則遠端主機不會收到ssh請求的。登陸上系統之后,會出現命令提示符:~$,即系統已經登陸到了遠端主機的shell,然后送去要執行的命令。完畢后推出遠程機器(這個send "exit\r"前也可以有上一個命令的輸出,也可以沒有,因為上一個命令執行完畢后會順序執行下一條)。
最后是等待標示子進程已結束的標示符eof,然后退出。(注:這個等待eof必須要有,如果沒有eof,很可能在子進程沒有結束前就退出,造成問題。)
下面的例子,介紹expect其他重要的命令,先看如下例子:
#!/usr/bin/expect -f
set timeout 10
set myname [lindex $argv 0]
spawn ./talk.sh
expect "Name:"
send "$myname\r"
expect eof
這個例子引入了輸入參數的概念,就是在執行這個expect腳本時候,需要帶入參數,比如這個腳本名字如果為test.ep
執行時需要鍵入參數:./test.ep "John"
set myname [lindex $argv 0] 這句獲取外部傳入的第一個參數(argv 0)並傳給變量myname,如果獲取多個參數則使用argv 1,argv 2,以此類推。
另外,expect支持一般語言所常用的if,for等流程控制語句,這個可以參看expect高級介紹
小結:
expect是一款非常好用的自動化交互工具
核心命令:
spawn: 啟動一個命令或程序,並由expect程序開始監聽
set :設置變量值和名稱
set .. lindex:從程序輸入參數出獲取變量值並賦給變量
expect ...send對:expect等待希望出現的匹配串,對於匹配到的串,send發送命令進行執行。
如何向expect腳本里面傳遞參數
#!/usr/bin/expect set timeout 10 set username [lindex $argv 0] set password [lindex $argv 1] set hostname [lindex $argv 2] spawn ssh-copy-id -i .ssh/id_rsa.pub $username@$hostname expect "yes/no" send "yes\r" expect "password:" send "$password\r" expect eof 執行腳本./ssh.exp root pasword hostname1
expect用法
1. [#!/usr/bin/expect]
這一行告訴操作系統腳本里的代碼使用那一個shell來執行。這里的expect其實和linux下的bash、windows下的cmd是一類東西。
注意:這一行需要在腳本的第一行。
2. [set timeout 30]
基本上認識英文的都知道這是設置超時時間的,現在你只要記住他的計時單位是:秒 。timeout -1 為永不超時
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]
執行完成后保持交互狀態,把控制權交給控制台,這個時候就可以手工操作了。如果沒有這一句登錄完成后會退出,而不是留在遠程終端上。如果你只是登錄過去執行
7.$argv 參數數組
expect腳本可以接受從bash傳遞過來的參數.可以使用[lindex $argv n]獲得,n從0開始,分別表示第一個,第二個,第三個....參數
下面的expect腳本的例子
執行這個文件./launch.exp 1 2 3
屏幕上就會分別打印出參數
send_user用來發送內容給用戶。
參數運用方面還有很多技巧
比如$argc 存儲了參數個數,args被結構化成一個列表存在argv。$argv0 被初始化為腳本名字。
除此之外,如果你在第一行(#!那行)使用-d (debug參數),可以在運行的時候輸出一些很有用的信息
比如你會看見
argv[0] = /usr/bin/expect argv[1] = -d argv[2] = ./launch.exp argv[3] = 1 argv[4] = 2 argv[5] = 3
使用這些也可以完成參數傳遞
8.
expect的命令行參數參考了c語言的,與bash shell有點不一樣。其中,$argc為命令行參數的個數,$argv0為腳本名字本身,$argv為命令行參數。[lrange $argv 0 0]表示第1個參數,[lrange $argv 0 4]為第一個到第五個參數。與c語言不一樣的地方在於,$argv不包含腳本名字本身。
9. exp_continue的用法
#!/usr/bin/expect -f
set ipaddr "localhost"
set passwd "iforgot"
spawn ssh root@$ipaddr #spawn 意思是執行命令,expect內命令,shell中不存在
expect {
"yes/no" { send "yes\r"; exp_continue}
"password:" { send "$passwd\r" }
}
expect "]# "
send "touch a.txt\r" #意思為發送命令
send "exit\r"
expect eof
exit
exp_continue可以繼續執行下面的匹配,簡單了許多。還有一點,讓我認識到匹配不見得要匹配最后幾個字符。
10.拿來小例子
設置變量 set PASSWD abcd123
#!/usr/bin/expect -f # Expect script to supply root/admin password for remote ssh server # and execute command. # This script needs three argument to(s) connect to remote server: # password = Password of remote UNIX server, for root user. # ipaddr = IP Addreess of remote UNIX server, no hostname # scriptname = Path to remote script which will execute on remote server # If you username and passwd has not pass the rsa trust, your login will fail. # Usage For example: # ./sshlogin.exp password 192.168.1.11 who # ------------------------------------------------------------------------ # Copyright (c) 2004 nixCraft project <http://cyberciti.biz/fb/> # This script is licensed under GNU GPL version 2.0 or above # ------------------------------------------------------------------------- # This script is part of nixCraft shell script collection (NSSC) # Visit http://bash.cyberciti.biz/ for more information. # ---------------------------------------------------------------------- # set Variables set password [lrange $argv 0 0] set ipaddr [lrange $argv 1 1] set scriptname [lrange $argv 2 2] set arg1 [lrange $argv 3 3] set timeout -1 # now connect to remote UNIX box (ipaddr) with given script to execute spawn ssh yourusername@$ipaddr $scriptname $arg1 match_max 100000 # Look for passwod prompt expect "*?assword:*" # Send password aka $password send -- "$password\r" # send blank line (\r) to make sure we get back to gui send -- "\r" expect eof
#!/usr/bin/expect
# 設置超時時間為 60 秒
set timeout 60
# 設置要登錄的主機 IP 地址
set host 192.168.1.46
# 設置以什么名字的用戶登錄
set name root
# 設置用戶名的登錄密碼
set password 123456
#spawn 一個 ssh 登錄進程
spawn ssh $host -l $name
# 等待響應,第一次登錄往往會提示是否永久保存 RSA 到本機的 know hosts 列表中;等到回答后,在提示輸出密碼;之后就直接提示輸入密碼
expect {
"(yes/no)?" {
send "yes\n"
expect "assword:"
send "$pasword\n"
}
"assword:" {
send "$password\n"
}
}
expect "#"
# 下面測試是否登錄到 $host
send "uname\n"
expect "Linux"
send_user "Now you can do some operation on this terminal\n"
# 這里使用了 interact 命令,使執行完程序后,用戶可以在 $host 終端進行交互操作。
Interact
用expect實現ssh自動登錄對服務器進行批量管理
1.實現ssh自動登錄完成任務的expect腳本
#!/usr/bin/expect -f
set ipaddress [lindex $argv 0]
set passwd [lindex $argv 1]
set timeout 30
spawn ssh shellqun@$ipaddress
expect {
"yes/no" { send "yes\r";exp_continue }
"password:" { send "$passwd\r" }
}
expect "*from*"
send "mkdir -p ./tmp/testfile\r"
#send "exit\r"
expect "#" 命令運行完, 你要期待一個結果, 結果就是返回shell提示符了(是# 或者$)
#最后一句第13行的解釋:
其實寫成 interact 的最大好處是登錄后不會退出,而會一直保持會話連接,可以后續手動處理其它任務,請根據實際情況自行選擇了。
2.調用login.exp完成批量管理
#!/bin/bash
for i in `awk '{print $1}' passwd.txt`
do
j=`awk -v I="$i" '{if(I==$1)print $2}' passwd.txt`
expect /root/shell/login.exp $i $j
done
linux下的expect的簡單用法及舉例
1、使用expect前,需要先安裝兩個rpm包,下載:http://download.csdn.net/detail/wang7dao/4416172
# rpm -ihv expect-5.43.0-8.el5.i386.rpm # rpm -ihv expect-devel-5.43.0-8.el5.i386.rpm
2、使用腳本文件的例子--實現自動輸密碼
#!/usr/bin/expect -f set password 123456 #download spawn scp root@192.168.1.218:/root/a.wmv /home/yangyz/ set timeout 300 expect "root@192.168.1.218's password:" set timeout 300 send "$password\r" set timeout 300 send "exit\r" expect eof www.2cto.com
3、在sh腳本中嵌入expect的例子--通過連上一個公網的服務器再轉跳到一個內網的服務器上,用腳本實現不用輸密碼,直接使用./goto.sh servername
#!/bin/bash passmsmallq10="a" passzhsh="a" passfcwr="b" passwapfx="c" passadfx="d" ip1="200.100.10.10" ip2="10.100.100.70" ip3="10.100.100.60" ip4="10.100.100.10" ip5="10.100.100.20" case $1 in "zhsh") passstr=$passzhsh ipstr=$ip2 ;; "fcwr") passstr=$passfcwr ipstr=$ip3 ;; "wapfx") passstr=$passwapfx ipstr=$ip4 ;; "adfx") passstr=$passadfx ipstr=$ip5 ;; *) echo "The parameter $1 isn't exist" exit 0 ;; www.2cto.com esac
command1="ssh -l m_smallq -p 36000 $ip1"
command2="ssh -l mqq -p 36000 $ipstr"
expect -c "
set timeout 60;
spawn $command1;
expect {
\"221.130.15.10's password:\" {send \"$passmsmallq10\r\"; exp_continue}
\"m_smallq\" {send \"$command2\r\"; exp_continue}
\"mqq's password:\" {send \"$passstr\r\";interact}
}
"
4、ssh到另一台機子執行df -h后退出,要點是send后面可以跟多個命令,通過\r來分行成多個命令
#!/bin/bash
ip1="183.62.178.191"
command1="ssh -l root -p 14322 $ip1"
expect -c "
spawn $command1;
expect {
\"183.62.178.191's password:\" {send \"aa\r\"; exp_continue}
\"root@\" {send \"df -h\r exit\r\"; exp_continue}
}
"
