自動化shell腳本except與python的pexpect模塊

expect腳本

expect是什麼

expect是一個免費的編程工具,用來實現自動的交互式任務,而無需人爲干預。說白了,expect就是一套用來實現自動交互功能的軟件。python

在實際工做中,咱們運行命令、腳本或程序時,這些命令、腳本或程序都須要從終端輸入某些繼續運行的指令,而這些輸入都須要人爲的手工進行。而利用expect,則能夠根據程序的提示,模擬標準輸入提供給程序,從而實現自動化交互執行linux

因爲在linux中的一些命令不太適合於腳本的自動化運行,好比fdisk,telnet,ftp鏈接下載等,因此必須使用except來解決交換問題。正則表達式

except基礎

包含如下四個命令shell

命令 做用
send 用於向進程發送字符串
except 從進程接收字符串
spwan 啓動新進程
interact 容許用戶交互
  • send命令接收一個字符串參數,並將該參數發送到進程。
  • expect命令和send命令相反,expect一般用來等待一個進程的反饋,咱們根據進程的反饋,再發送對應的交互命令。
  • spawn命令用來啓動新的進程,spawn後的send和expect命令都是和使用spawn打開的進程進行交互。
  • interact命令用的其實不是不少,通常狀況下使用spawn、send和expect命令就能夠很好的完成咱們的任務;但在一些特殊場合下仍是須要使用interact命令的,interact命令主要用於退出自動化,進入人工交互。好比咱們使用spawn、send和expect命令完成了ftp登錄主機,執行下載文件任務,可是咱們但願在文件下載結束之後,仍然能夠停留在ftp命令行狀態,以便手動的執行後續命令,此時使用interact命令就能夠很好的完成這個任務。

代碼舉例

#!/usr/bin/expect

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

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

這是一段很是簡單代碼,演示了基本用法編程

#!/usr/bin/expect:使用expect來解釋該腳本;
set timeout 30:設置超時時間,單位爲秒,默認狀況下是10秒;
set host "101.200.241.109":設置變量;
spawn ssh $username@$host:spawn是進入expect環境後才能夠執行的expect內部命令,若是沒有裝expect或者直接在默認的SHELL下執行是找不到spawn命令的。它主要的功能是給ssh運行進程加個殼,用來傳遞交互指令;
expect "password":這裏的expect也是expect的一個內部命令,這個命令的意思是判斷上次輸出結果裏是否包含「password」的字符串,若是有則當即返回;不然就等待一段時間後返回,這裏等待時長就是前面設置的30秒;
send "$password\r":當匹配到對應的輸出結果時,就發送密碼到打開的ssh進程,執行交互動做;
interact:執行完成後保持交互狀態,把控制權交給控制檯,這個時候就能夠手工操做了。若是沒有這一句登陸完成後會退出,而不是留在遠程終端上。windows

這就是對上述這段簡單簡單腳本的分析,在上述的示例中,涉及到expect中一個很是重要的概念——模式-動做;即上述expect "password" {send "$password\r"}這句代碼表達出來的含義。bash

模式-動做

結合着expect "password" {send "$password\r"}這句代碼來講說「模式-動做」。簡單的說就是匹配到一個模式,就執行對應的動做;匹配到password字符串,就輸入密碼ssh

以下所示:xss

expect {
    "password" {
        send "$password\r"
        exp_continue
    }
    eof
    {
        send "eof"
    }
}

其中exp_continue表示循環式匹配,一般匹配以後都會退出語句,但若是有exp_continue則能夠不斷循環匹配,輸入多條命令,簡化寫法。ide

傳參

不少時候,咱們須要傳遞參數到腳本中,如今經過下面這段代碼來看看如何在expect中使用參數:

#!/usr/bin/expect

if {$argc < 3} {
    puts "Usage:cmd <host> <username> <password>"
    exit 1
}

set timeout -1
set host [lindex $argv 0] 
set username [lindex $argv 1]
set password [lindex $argv 2]

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

在expect中,\$argc表示參數個數,而參數值存放在$argv中,好比取第一個參數就是[lindex $argv 0],以此類推。

FTP下載expect腳本

使用yum安裝expect

yum install expect

按照以下編寫expect腳本

#!/usr/bin/expect -f  
set ip [lindex $argv 0]                                                     #腳本的第一個參數,遠程主機的IP地址
set file [lindex $argv 1]                                                   #腳本的第二個參數,指定下載的文件名
set timeout 10                                                                #設置超時時間10秒
spawn ftp $ip                                                                 #運行ftp $ip命令
expect "Name*"                                                             #若是出現Name字符
send "anonymous \r"                                                     #則輸入anoymous(匿名用戶)並回車
expect "Password:*"                                                      #若是出現Password字符
send "\r"                                                                         #則僅輸入回車
expect "ftp>*"                                                                 #若是出現ftp>字符
send "get $file\r"                                                             #則發送get $file命令
expect {
    "*Failed*" { send_user " Download failed\r";send "quit\r"}   #若是返回的字符串有Failed,則說明下載失敗,send_user回顯信息 Download failed 
    "*send*" { send_user " Download ok\r";send "quit\r"}  #若是返回的字符串有send,則說明下載失敗,send_user回顯信息 Download ok
    }
expect eof    #結束循環匹配

給腳本加上可執行權限chmod +x expect_ftp_auto.exp

pexpect模塊

pexpect能夠理解爲linux下的expect的python封裝,經過pexpect能夠實現ssh,ftp,passwd,telnet等命令的進行自動交互

安裝pip install pexpect

簡單實現ssh自動登陸的示例以下:

import pexpect
child = pexpect.spwan('scp foo user#expample.com:.')   #spwan啓動scp程序
child.expect('Password:')                   #expect方法等待子程序產生的輸出,判斷是否匹配定義的字符串
                                                                                       #‘Password:'
child.sendline(mypassword)             #匹配後則發送密碼進行迴應

核心組件

spawn類

spawn是pexpect的主要入口,功能是啓動和控制子應用程序,如下是它的構造函數

class pexpect.spwan(command,args=[],timeout=30,maxread=2000,searchwindowsize=None,logfile=None,cwd=None,env=None,ignore_sighup=True)

其中,command參數能夠是任意已知的系統命令,好比

child=pexpect.spawn('user/bin/ftp')

當子程序須要參數的時候,還可使用python列表來代替參數,如

child = pexpect.spwan('user/bin/ssh user@example.com')

參數timeout爲等待結果的超時時間,maxread爲從終端控制檯一次讀取的最大字節數,searchwindowsize參數爲匹配的緩衝區字符串的位置,默認是從開始位置匹配

須要注意的是,pexpext不會解析shell命令中的元字符,包括重定向> 管道|或者通配符,此時能夠將三個特殊元字符的命令做爲/bin/bash的參數進行調用

child =expect.spwan('/bin/bash -c "ls -l | grep LOG> logs.txt"')
child.expect(pexpect.EOF)

能夠經過將命令的參數以PYTHON列表的方式進行替換,從而使得語法更加清晰,下面的代碼等同於上面的代碼

shell_cmd='ls -l | grep LOG >logs.txt'
child=pexpext.spwan('/bin/bash',['-c,shell_cmd])
child.expect(pexpect.EOF)

在調試代碼時,但願獲取pexpect的輸入與輸出信息,以便了解匹配的狀況,一種時寫到日誌中,另外一種時輸出到標準輸出

寫到日誌中

chidl-pexpect.spwan('some_command')
fout=file('mylog.txt,'w')
child.logfile=fout

輸出到標準輸出的方法

child=pexpect.swpan('some_command')
chuld.logfile=sys.stdout

如下爲SSH遠程登陸舉例,登陸成功後顯示/home目錄的文件而且記錄輸入與輸出

import pexpect
import sys

child=pexpect.spawn('ssh root@172.31.208.129')
fout=open('mylog.txt','w')
child.logfile=fout
#child.logfile=sys.stdout

child.expect('password:')
child.sendline('abc@123')
child.expect('#')
child.sendline('ls /home')
child.expect('#')

expect方法

expect定義了子程序輸出的匹配規則
方法定義:expect(pattern,timeout=-1,searchwindowsize=-1)
其中,參數pattern表示字符串,pexpext.EOF(指向緩衝區,無匹配項)、pexpect,TIMEOUT(匹配等待超時),正則表達式或者列表

參數timeout指定了等待匹配結果的超時時間,單位爲秒,當超時被觸發的時候,expect將匹配到pexpext.TIMEOUT,參數searchwindowsize爲匹配的緩衝字符串的位置,默認時從開始的位置匹配

read相關方法

下面的的方法做用都是向子程序發送響應命令

send(self,s)     #發送命令,不回車
sendline(self,s=' '),#發送命令,回車
snedcontrol(self.char) #發送控制字符
sendeof()   #發送eof

run函數

run時使用pexpext進行封裝的調用外部命令的的函數

from pexpect import *
run('scp foo user@example.com:.',events={'(?i)password':mypassword])

pxssh類

針對ssh會話操做上再作一次封裝

class pexpext.pxssh.pxssh(timeout=30,maxread=2000,searchwindwosize=None,logfile=None,cwd=None,env=None)

經常使用方法

login()創建ssh鏈接
logout() 斷開鏈接
promp()等待系統提示符,用於等待命令執行結束

import pxssh
import getpass
try:
    s = pxssh.pxssh()                                  #建立對象s
    hostname = raw_input('hostname: ')
    username = raw_input('username: ')
    password = getpass.getpass('password: ')   #接收密碼輸入
    s.login (hostname, username, password)     #創建ssh鏈接
    s.sendline ('uptime')  # 運行uptime命令
    s.prompt()             # 匹配系統提示符
    print s.before         # 打印系統體術符號出現前的命令輸出
    s.sendline ('ls -l')
    s.prompt()
    print s.before
    s.sendline ('df')
    s.prompt()
    print s.before
    s.logout()
except pxssh.ExceptionPxssh, e:
    print "pxssh failed on login."
    print str(e)

FTP自動操做

實現自動交互登陸FTP操做

import pexpect
import sys

child = pexpect.spawnu('ftp ftp.openbsd.org')  #運行ftp命令
child.expect('(?i)name .*: ')     #(?!)表示後面的字符串正則表達式忽略大小寫
child.sendline('anonymous')   # 輸入ftp帳號信息
child.expect('(?i)password')    #匹配密碼提示
child.sendline('pexpect@sourceforge.net')   
child.expect('ftp> ')
child.sendline('bin')     #啓用二進制傳輸
child.expect('ftp> ')
child.sendline('get robots.txt')  
child.expect('ftp> ')
sys.stdout.write (child.before)    #輸出匹配的"ftp"以前的輸入與輸出操做
print("Escape character is '^]'.\n")
sys.stdout.write (child.after)
sys.stdout.flush()
child.interact() # Escape character defaults to ^]   #讓出控制權,用戶能夠繼續當前會話手工控制子程序,默認輸入"^]"字符跳出
child.sendline('bye')
child.close()
相關文章
相關標籤/搜索