系统批量运维管理器pexpect详解
一、pexpect介紹
pexpect可以理解成Linux下的expect的Python封裝,通過pexpect我們可以實現(xiàn)對ssh、ftp、passwd、telnet等命令進行自動交互,而無需人工干涉來達到自動化的目的。比如我們可以模擬一個FTP登陸時的所有交互,包括輸入主機地址、用戶名、密碼、上傳文件等,待出現(xiàn)異常我們還可以進行嘗試自動處理。
pexpect官網(wǎng)地址:https://pexpect.readthedocs.io/en/stable/
https://pypi.org/project/pexpect/
二、pexpect的安裝
mac os安裝
pip3 install pexpect
CentOS 安裝
[root@localhost ~]# pip3 install pexpect
Collecting pexpect
Downloading https://files.pythonhosted.org/packages/89/e6/b5a1de8b0cc4e07ca1b305a4fcc3f9806025c1b651ea302646341222f88b/pexpect-4.6.0-py2.py3-none-any.whl (57kB)
100% |████████████████████████████████| 61kB 128kB/s
Collecting ptyprocess>=0.5 (from pexpect)
Downloading https://files.pythonhosted.org/packages/ff/4e/fa4a73ccfefe2b37d7b6898329e7dbcd1ac846ba3a3b26b294a78a3eb997/ptyprocess-0.5.2-py2.py3-none-any.whl
Installing collected packages: ptyprocess, pexpect
Successfully installed pexpect-4.6.0 ptyprocess-0.5.2
三、pexpect的核心組件
spawn類
spawn是pexpect的主要類接口,功能是啟動和控制子應用程序,以下是它的構(gòu)造函數(shù)定義:
class pexpect.spawn(command, args=[], timeout=30, maxread=2000, searchwindowsize=None, logfile=None, cwd=None, env=None, ignore_sighup=True)
其中command參數(shù)可以是任意已知的系統(tǒng)命令,比如:
child = pexpect.spawn('/usr/bin/ftp') #啟動FTP客戶端命令
child = pexpect.spawn('/usr/bin/ssh user@example.com') #啟動ssh遠程連接命令
child = pexpect.spawn('ls -latr /tmp') #運行l(wèi)s顯示/tmp目錄內(nèi)容命令
當子程序需要參數(shù)時,還可以使用Python列表代替參數(shù)項,如:
child = pexpect.spawn('/usr/bin/ftp', [])
child = pexpect.spawn('/usr/bin/ssh', ['user@example.com'])
child = pexpect.spawn('ls', ['-latr', '/tmp'])
參數(shù)timeout為等待結(jié)果的超時時間;參數(shù)maxread為pexpect從終端控制臺一次讀取的最大字節(jié)數(shù),searchwindowsize參數(shù)為匹配緩沖區(qū)字符串的位置,默認是從開始位置匹配。
需要注意的是,pexpect不會解析shell命令當中的元字符,包括重定向">"、管道"|"或通配符“*”,當然,我們可以通過一個技巧來解決這個問題,將存在著三個特殊元字符的命令作為/bin/bash的參數(shù)進行調(diào)用,例如:
child = pexpect.spawn('/bin/bash -c "ls -l | grep LOG > logs.txt"')
child.expect(pexpect.EOF)
我們可以通過將命令的參數(shù)以Python列表的形式進行替換,從而使我們的語法變成更加清晰,下面的代碼等價于上面的。
shell_cmd = 'ls -l | grep LOG > logs.txt'
child = pexpect.spawn('/bin/bash', ['-c', shell_cmd])
child.expect(pexpect.EOF)
有時候調(diào)式代碼時,希望獲取pexpect的輸入與輸出信息,以便了解匹配的情況。pexpect提供了兩種途徑,一種為寫到日志文件,另一種為輸出到標準輸出。寫到日志文件的方法如下:
child = pexpect.spawn('some_command')
fout = open('mylog.txt','wb')
child.logfile = fout
輸出到標準輸出的方法如下:
# In Python 2:
child = pexpect.spawn('some_command')
child.logfile = sys.stdout
# In Python 3, we'll use the ``encoding`` argument to decode data
# from the subprocess and handle it as unicode:
child = pexpect.spawn('some_command', encoding='utf-8')
child.logfile = sys.stdout
logfile_read和logfile_send成員可用于分別記錄來自子項的輸入和發(fā)送給子項的輸出。有時你不想看到你給孩子寫的所有內(nèi)容。你只想記錄孩子送回的東西。例如:
child = pexpect.spawn('some_command')
child.logfile_read = sys.stdout
如果您使用的是Python 3,則需要在上面的代碼中傳遞編碼。
要單獨記錄發(fā)送給子級的輸出使用logfile_send:
child.logfile_send = fout
注意:如果你想獲得孩子的退出狀態(tài),你必須調(diào)用close()方法。孩子的出口或信號狀態(tài)將存儲在self.exitstatus或self.signalstatus中。如果孩子正常退出,exitstatus將存儲退出返回碼,signalstatus將為None。如果孩子被信號異常終止,那么signalstatus將存儲信號值,exitstatus將為None:
child = pexpect.spawn('some_command')
child.close()
print(child.exitstatus, child.signalstatus)
更多細節(jié),可以讀取存儲由os.waitpid返回的狀態(tài)的self.status成員。你可以使用os.WIFEXITED/os.WEXITSTATUS 或 os.WIFSIGNALED/os.TERMSIG來解釋它。
下面為一個完整的示例,實現(xiàn)遠程SSH登錄,登錄成功后顯示/usr/local/src/目錄文件清單,并通過日志文件記錄所有的輸入與輸出。
import pexpect
import sys
child = pexpect.spawn('ssh root@192.168.56.132')
fout = open('mylog.txt',mode='wb')
child.logfile = fout
#child.logfile = sys.stdout
child.expect("(yes/no)?")
child.sendline("yes")
child.expect("password:")
child.sendline("1234567")
child.expect('#')
child.sendline('/bin/ls /usr/local/src/')
child.expect("#")
以下為mylog.txt日志內(nèi)容,可以看到pexpect產(chǎn)生的全部輸入與輸出信息。
[root@localhost ~]# cat mylog.txt yes yes root@192.168.56.132's password: 1234567 Last login: Sat Jun 2 15:13:51 2018 from 192.168.56.131 [root@localhost ~]# /bin/ls /usr/local/src/ /bin/ls /usr/local/src/ Python-3.6.5 Python-3.6.5.tgz
expect方法
expect定義了一個子程序輸出的匹配規(guī)則。
方法定義:expect(pattern,timeout=-1,searchwindowsize=-1,async_=False,**kw)
其中,參數(shù)pattern表示字符串、pexpect.EOF(指向緩沖區(qū)尾部,無匹配項)、pexpect.TIMEOUT(匹配等待超時)、正則表達式或者前面四種類型組成的列表(List),當pattern為一個列表時,且不止一個列表元素被匹配,則返回的結(jié)果是子程序輸出最先出現(xiàn)的那個元素,或者是列表最左邊的元素(最小索引ID),如:
import pexpect
child = pexpect.spawn("echo 'foobar'")
print(child.expect(['bar','foo','foobar']))
#輸出:1即'foo'被匹配
參數(shù)timeout指定等待匹配結(jié)果的超時時間,單位為秒。當超時被觸發(fā)時,expect將匹配到pexpect.TIMEOUT;參數(shù)searchwindowsize為匹配緩存區(qū)字符串的位置,默認是從開始位置匹配。
當pexpect.EOF、pexpect.TIMEOUT作為expect的列表參數(shù)時,匹配時將返回所處列表中索引ID,例如:
index = p.expect(['good', 'bad', pexpect.EOF, pexpect.TIMEOUT])
if index == 0:
do_something()
elif index == 1:
do_something_else()
elif index == 2:
do_some_other_thing()
elif index == 3:
do_something_completely_different()
以上代碼等價于
try:
index = p.expect(['good', 'bad'])
if index == 0:
do_something()
elif index == 1:
do_something_else()
except EOF:
do_some_other_thing()
except TIMEOUT:
do_something_completely_different()
expect方法有兩個非常棒的成員:befoe與。before成員保存了最近匹配成功之前的內(nèi)容,affer成員保存了最近匹配成功之后的內(nèi)容。例如:
import pexpect
child = pexpect.spawn('ssh root@192.168.56.132',encoding='utf-8')
fout = open('mylog.txt',mode='w')
child.logfile = fout
child.expect("(yes/no)?")
child.sendline("yes")
child.expect(['password:'])
child.sendline("1234567")
print("before:"+child.before)
print("after:"+child.after)
運行結(jié)果如下:
[root@localhost ~]# python3 simple2.py before:yes root@192.168.56.132's after:password:
read相關(guān)方法
下面這些輸入方法的作用都是向子程序發(fā)送響應命令,可以理解成代替了我們的標準輸入鍵盤。
send(self,s) 發(fā)送命令,不回車
sendline(self, s='') 發(fā)送命令,回車
sendcontrol(self, char) 發(fā)送控制字符,如child.sendcontrol('c')等價于"ctrl+c"
sendof() 發(fā)送eof
四、run函數(shù)
run是使用pexpect進行封裝的調(diào)用外部命令的函數(shù),類似于os.system或os.popen方法,不同的是使用run()可以同時獲得命令的輸出結(jié)果及命令的退出狀態(tài),函數(shù)定義:
pexpect.run(command, timeout=30, withexitstatus=False, events=None, extra_args=None, logfile=None, cwd=None, env=None, **kwargs)。
參數(shù)command可以是系統(tǒng)已知的任意命令,如沒有寫絕對路徑時將會嘗試搜索命令的路徑,events是一個字典,定義了expect及sendline方法的對應關(guān)系,spawn方式的例子如下:
from pexpect import *
child = spawn('scp foo user@example.com:.')
child.expect('(?i)password')
child.sendline(mypassword)
使用run函數(shù)實現(xiàn)如下,是不是更加簡潔、精煉了?
from pexpect import *
run('scp foo user@example.com:.', events={'(?i)password': mypassword})
pxssh類
該類將pexpect.spawn擴展為專門設(shè)置SSH連接。增加了登錄,注銷和期望的shell提示的方法。它執(zhí)行各種棘手的事情來處理SSH登錄過程中的許多情況。例如,如果會話是您第一次登錄,則pxssh會自動接收遠程證書;或者你有公鑰認證設(shè)置,則pxssh不會等待密碼提示。 pxssh使用shell提示符來同步來自遠程主機的輸出。為了使它更健壯,它將shell提示設(shè)置為比$或#更獨特的東西。這應該適用于大多數(shù)Borne/Bash or Csh 風格的 shell。
pxssh類定義:
classpexpect.pxssh.pxssh(timeout=30,maxread=2000,searchwindowsize=None,logfile=None,cwd=None,env=None,ignore_sighup=True,echo=True,options={},encoding=None,codec_errors='strict',debug_command_string=False)
pxssh常用的三個方法如下:
login()建立ssh連接;
logout()斷開連接;
prompt()等待系統(tǒng)提示符,用于等待命令執(zhí)行結(jié)束。
下面使用pxssh類實現(xiàn)一個ssh連接遠程主機并執(zhí)行命令的示例。首先使用login()方法與遠程主機建立連接,再通過sendline()方法發(fā)送執(zhí)行的命令,prompt()方法等待命令執(zhí)行結(jié)束且出現(xiàn)系統(tǒng)提示符,最后使用logout()方法斷開連接。
from pexpect import pxssh
import getpass
try:
s = pxssh.pxssh() #創(chuàng)建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() #匹配系統(tǒng)提示符
print(s.before) #打印出現(xiàn)系統(tǒng)提示符前的命令輸出
s.sendline('ls -l')
s.prompt()
print(s.before)
s.sendline('df')
s.prompt()
print(s.before)
s.logout() #斷開ssh連接
except pxssh.ExceptionPxssh as e:
print("pxssh failed on login.")
print(e)
五、pexpect應用實例
遠程文件自動打包并下載
import pexpect
import sys
ip='192.168.56.132' #定義目標主機
user='root' #目標主機用戶
passwd='1234567' #目標主機密碼
target_file='/data/logs/nginx_access.log' #目標主機nginx日志文件
child = pexpect.spawn('/usr/bin/ssh', [user+'@'+ip],encoding='utf-8') #運行ssh命令
fout = open('mylog.txt','w') #輸入、輸出日志寫入mylog.txt文件
child.logfile = fout
try:
child.expect('(?i)password') #匹配password字符串,(?i)表示不區(qū)別大小寫
child.sendline(passwd)
child.expect('#')
child.sendline('tar -zcPf /data/nginx_access.tar.gz ' +target_file) #打包nginx日志文件
child.expect('#')
print(child.before)
child.sendline('exit')
fout.close()
except EOFError: #定義EOF異常處理
print('expect EOF')
except TimeoutError: #定義timeout異常處理
print('expect timeout')
child = pexpect.spawn('/usr/bin/scp', [user+'@'+ip+':/data/nginx_access.tar.gz','/home'],encoding='utf-8') #啟動scp遠程拷貝命令,實現(xiàn)將打包好的nginx日志復制到本地/home目錄下
fout = open('mylog.txt','a')
child.logfile = fout
try:
child.expect('(?i)password')
child.sendline(passwd)
child.expect(pexpect.EOF) #匹配緩沖區(qū)EOF(結(jié)尾),保證文件復制正常完成
except EOFError:
print('expect EOF')
except TimeoutError:
print('expect timeout')
總結(jié)
以上是生活随笔為你收集整理的系统批量运维管理器pexpect详解的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 股票配债怎么操作
- 下一篇: 蔡康永的家庭背景(蔡康永同性恋)