使用expect編寫腳本

筆記內容:java

  • 20.28 expect腳本遠程登陸
  • 20.29 expect腳本遠程執行命令
  • 20.30 expect腳本傳遞參數
  • 20.31 expect腳本同步文件
  • 20.32 expect腳本指定host和要同步的文件

筆記日期:2017-11-29mysql


<br>正則表達式

expect簡介


expect是一個自動化交互式應用程序的工具,因此expect能夠用來處理交互的命令。藉助expect,咱們能夠將交互過程寫在一個腳本上,使之自動化完成。形象的說,ssh登陸,ftp登陸等都符合交互的定義。expect含有利用正則表達式進行模式匹配以及通用的編程功能,容許簡單的腳本智能地管理以下工具:telnet,ftp和ssh(這些工具都缺乏編程的功能),宏以及其它程序。expect腳本的出現使得這些老的軟件工具備了新的功能和更多的靈活性。sql

<br>shell

expect腳本遠程登陸


以上簡介中也提到了expect和shell相似,能夠進行編程,接下來就實際的編寫一些expect腳原本學習expect簡單的使用方式。編程

若是你的系統沒有安裝expect,須要先安裝expect,安裝命令以下:vim

yum install -y expect數組

expect示例:編寫一個自動遠程登陸腳本,expect編寫的腳本文件後綴名爲expect,這是爲了好區分:bash

[root@localhost ~]# mkdir expectFiles
[root@localhost ~]# cd expectFiles/
[root@localhost ~/expectFiles]# vim telnet_ept.expect
#! /usr/bin/expect
# 須要登陸的IP
set host "192.168.77.128"
# 密碼
set passwd "123456"
# 登陸的命令
spawn ssh root@$host
# expect用於執行一些命令
expect {
# 初次登陸會詢問"yes/no",因此若是截取的是"yes/no"就 send 發送一個yes,\r是回車的意思,exp_continue表示繼續
"yes/no" { send "yes\r"; exp_continue}
# 若是截取的是"password:"就 send 發送密碼
"password:" { send "$passwd\r" }
}
# 結束標識,可是不會退出登陸,不加interact的話就會立刻退出登陸
interact

set是用來定義一個變量的,變量名後面跟的就是變量的值,不須要使用等於號來賦值,調用變量的方式和shell同樣,也是使用$符號。ssh

與interact相對的就是expect eof ,expect eof會在登陸後,停留登陸狀態1-2秒後才退出登陸。

執行這個腳本須要加一個執行權限,而後直接執行就能夠了:

[root@localhost ~/expectFiles]# chmod a+x telnet_ept.expect 
[root@localhost ~/expectFiles]# ./telnet_ept.expect 
spawn ssh root@192.168.77.128
The authenticity of host '192.168.77.128 (192.168.77.128)' can't be established.
ECDSA key fingerprint is 4d:5a:9d:31:65:75:30:47:a3:9c:f5:56:63:c4:0f:6a.
Are you sure you want to continue connecting (yes/no)? yes
Warning: Permanently added '192.168.77.128' (ECDSA) to the list of known hosts.
root@192.168.77.128's password: 
Last login: Wed Nov 29 14:07:45 2017 from 192.168.77.1
[root@localhost ~]#

運行結果如上,成功登陸了192.168.77.128。

<br>

expect腳本遠程執行命令


除了可以實現遠程登陸外,還能實現遠程執行命令,例如能夠寫一個自動遠程登陸後,執行命令並退出的腳本:

[root@localhost ~/expectFiles]# vim telnet_2.expect
#!/usr/bin/expect
set user "root"
set passwd "123456"
spawn ssh $user@192.168.77.128

expect {
"yes/no" { send "yes\r"; exp_continue}
"password:" { send "$passwd\r" }
}
# 當遇到 "]#" 就執行sebd命令,咱們登陸後不就是:[root@localhost ~]#,末尾就是 ]# ,而send是發送touch建立文件的命令
expect "]#"
send "touch /tmp/12.txt\r"
# 一樣的當遇到 "]#" 就執行sebd裏的命令
expect "]#"
send "echo 1212 > /tmp/12.txt\r"
# 最後退出系統
expect "]#"
send "exit\r"

運行腳本:

[root@localhost ~/expectFiles]# chmod a+x telnet_2.expect 
[root@localhost ~/expectFiles]# ./telnet_2.expect
spawn ssh root@192.168.77.128
root@192.168.77.128's password: 
Last login: Wed Nov 29 14:54:34 2017 from 192.168.77.130
[root@localhost ~]# touch /tmp/12.txt
[root@localhost ~]# echo 1212 > /tmp/12.txt
[root@localhost ~]# 
[root@localhost ~/expectFiles]#

而後再來看看192.168.77.128機器上有沒有/tmp/12.txt 文件:

[root@localhost ~]# ll /tmp/12.txt 
-rw-r--r-- 1 root root 5 11月 29 15:10 /tmp/12.txt
[root@localhost ~]# cat !$
cat /tmp/12.txt
1212
[root@localhost ~]#

能夠看到文件和內容與腳本中執行的命令一致。

<br>

expect腳本傳遞參數


expect腳本也是能夠像shell腳本同樣傳遞參數的,如下例子演示如何拿到腳本的參數:

[root@localhost ~/expectFiles]# vim telnet_3.expect
#!/usr/bin/expect
# expect腳本拿參數的方式有點像是在數組中拿元素同樣,也是從0開始數,$argv是expect的一個內置變量
set user [lindex $argv 0] # 第一個參數
set host [lindex $argv 1]  # 第二個參數
set passwd "123456"
# 須要遠程執行的命令
set cm [lindex $argv 2]  # 第三個參數
spawn ssh $user@$host

expect {
"yes/no" { send "yes\r"}
"password:" { send "$passwd\r" }
}
# 執行命令而後退出登陸
expect "]#"
send "$cm\r"
expect "]#"
send "exit\r"

以上腳本運行結果:

[root@localhost ~/expectFiles]# ./telnet_3.expect root 192.168.77.128 ls
spawn ssh root@192.168.77.128
root@192.168.77.128's password: 
Last login: Wed Nov 29 15:10:48 2017 from 192.168.77.130
[root@localhost ~]# ls
\                                           stop.sh     zabbix-release-3.2-1.el7.noarch.rpm
anaconda-ks.cfg                             Test.class
mysql57-community-release-el7-8.noarch.rpm  Test.java
[root@localhost ~]# 
[root@localhost ~/expectFiles]#

執行多條命令,使用引號將命令括起來便可:

[root@localhost ~/expectFiles]# ./telnet_3.expect root 192.168.77.128 "ls;w;who"
spawn ssh root@192.168.77.128
root@192.168.77.128's password: 
Last login: Wed Nov 29 15:29:30 2017 from 192.168.77.130
[root@localhost ~]# ls;w;who
\                                           stop.sh     zabbix-release-3.2-1.el7.noarch.rpm
anaconda-ks.cfg                             Test.class
mysql57-community-release-el7-8.noarch.rpm  Test.java
 15:29:48 up  5:13,  2 users,  load average: 0.00, 0.01, 0.05
USER     TTY      FROM             LOGIN@   IDLE   JCPU   PCPU WHAT
root     pts/0    192.168.77.1     14:07   15:00   0.06s  0.06s -bash
root     pts/1    192.168.77.130   15:29    0.00s  0.02s  0.00s w
root     pts/0        2017-11-29 14:07 (192.168.77.1)
root     pts/1        2017-11-29 15:29 (192.168.77.130)
[root@localhost ~]# [root@localhost ~/expectFiles]#

關於這種遠程執行命令的操做,會存在一個超時時間,也就是說,若是你須要遠程執行的那條命令的執行時間,超過了默認的超時時間,命令的執行就會被終止。這個默認的超時時間是10秒,咱們能夠經過執行一條vmstat命令來測試出這個默認的超時時間:

[root@localhost ~/expectFiles]# ./telnet_3.expect root 192.168.77.128 "vmstat 1"
spawn ssh root@192.168.77.128
root@192.168.77.128's password: 
Last login: Wed Nov 29 17:25:48 2017 from 192.168.77.130
[root@localhost ~]# vmstat 1
procs -----------memory---------- ---swap-- -----io---- -system-- ------cpu-----
 r  b   swpd   free   buff  cache   si   so    bi    bo   in   cs us sy id wa st
 1  0      0 3271556   1376 259272    0    0     4     1   35   64  0  0 100  0  0
 0  0      0 3271712   1376 259364    0    0     0     0   92  157  0  0 100  0  0
 0  0      0 3271712   1376 259364    0    0     0     0   76  128  0  0 100  0  0
 0  0      0 3271744   1376 259364    0    0     0     0   91  146  0  1 100  0  0
 0  0      0 3271744   1376 259364    0    0     0     0   64  120  0  0 100  0  0
 0  0      0 3271712   1376 259364    0    0     0     0   78  127  0  0 100  0  0
 0  0      0 3271712   1376 259364    0    0     0     0   57  104  0  0 100  0  0
 0  0      0 3271712   1376 259364    0    0     0     0   81  128  0  1 100  0  0
 0  0      0 3271712   1376 259364    0    0     0     0   75  127  0  0 100  0  0
 0  0      0 3271200   1376 259400    0    0     0     0   91  153  0  1 100  0  0
 0  0      0 3270936   1376 259400    0    0     0     0   92  152  0  0 100  0  0
[root@localhost ~/expectFiles]#

以上運行結果能夠看到vmstat設置的間隔時間爲1秒,只打印了11行數據就退出登陸了,第一行打印不算在超時時間內,從第二行開始數,恰好是10行,每行打印的間隔時間是1秒,因此就能夠知道默認的超時時間是10秒。

那麼要如何修改這個默認的超時時間呢?只須要在腳本中加上一句set timeout便可,設置的時間單位爲秒,示例:

#!/usr/bin/expect
set user [lindex $argv 0]
set host [lindex $argv 1]
set passwd "Zero-One1."
set cm [lindex $argv 2]
spawn ssh $user@$host
# 設置超時時間爲3秒
set timeout 3

expect {
"yes/no" { send "yes\r"}
"password:" { send "$passwd\r" }
}
expect "]#"
send "$cm\r"
expect "]#"
send "exit\r"

運行結果:

[root@localhost ~/expectFiles]# ./telnet_3.expect root 192.168.77.128 "vmstat 1"
spawn ssh root@192.168.77.128
root@192.168.77.128's password: 
Last login: Wed Nov 29 17:25:54 2017 from 192.168.77.130
[root@localhost ~]# vmstat 1
procs -----------memory---------- ---swap-- -----io---- -system-- ------cpu-----
 r  b   swpd   free   buff  cache   si   so    bi    bo   in   cs us sy id wa st
 1  0      0 3271172   1376 259420    0    0     4     1   35   64  0  0 100  0  0
 0  0      0 3270968   1376 259508    0    0     0     0  104  174  0  0 100  0  0
 0  0      0 3270936   1376 259560    0    0     0     0   90  152  0  1 100  0  0
 0  0      0 3270780   1376 259596    0    0     0     0   83  137  0  0 100  0  0
[root@localhost ~/expectFiles]#

提示:若是你將set timeout的值設置爲-1的話,就表示沒有超時時間,命令會一直執行。

<br>

expect腳本同步文件


expect結合rsync 能夠實現自動同步文件,代碼示例:

[root@localhost ~/expectFiles]# vim synFile.expect
#!/usr/bin/expect
set passwd "123456"
spawn rsync -av root@192.168.77.128:/tmp/12.txt /tmp/

expect {
"yes/no" { send "yes\r"}
"password:" { send "$passwd\r" }
}
expect eof

以上腳本運行結果:

[root@localhost ~/expectFiles]# chmod a+x synFile.expect
[root@localhost ~/expectFiles]# ./synFile.expect 
spawn rsync -av root@192.168.77.128:/tmp/12.txt /tmp/
root@192.168.77.128's password: 
receiving incremental file list
12.txt

sent 30 bytes  received 84 bytes  228.00 bytes/sec
total size is 5  speedup is 0.04
[root@localhost ~/expectFiles]# cat /tmp/12.txt 
1212
[root@localhost ~/expectFiles]#

若是把腳本中的expect eof命令給註釋掉,看看會發生什麼:

[root@localhost ~/expectFiles]# ./synFile.expect 
spawn rsync -av root@192.168.77.128:/tmp/12.txt /tmp/
root@192.168.77.128's password: 
[root@localhost ~/expectFiles]#

從結果能夠看到,還沒來得及執行rsync命令就退出來了,因此在這種登陸後須要執行命令的expect腳本中必定要加上expect eof或者interact,否則你須要遠程執行的命令就會來不及被執行,這就是加不加expect eof或者interact的區別。

可能有些人會想到使用set timeout來設置一個超時時間避免立刻退出登陸,其實是無效的,避免這種立刻退出登陸的方式只能用expect eof或者interact。

<br>

expect腳本指定host和要同步的文件


想要指定host和要同步的文件就把它們做爲參數就行了:

#!/usr/bin/expect
set passwd "123456"
set host [lindex $argv 0]
set file [lindex $argv 1]
spawn rsync -av $file root@$host:$file

expect {
"yes/no" { send "yes\r"}
"password:" { send "$passwd\r" }
}

expect eof

運行結果:

[root@localhost ~/expectFiles]# chmod a+x hostFile.expect 
[root@localhost ~/expectFiles]# ./hostFile.expect 192.168.77.128 "/tmp/12.txt"
spawn rsync -av /tmp/12.txt root@192.168.77.128:/tmp/12.txt
root@192.168.77.128's password: 
sending incremental file list

sent 31 bytes  received 12 bytes  86.00 bytes/sec
total size is 5  speedup is 0.12
[root@localhost ~/expectFiles]#

文件的路徑要寫絕對路徑,而後須要使用雙引號引發來。

相關文章
相關標籤/搜索