Pexpect 是 Don Libes 的 Expect 語言的一個 Python 實現,是一個用來啟動子程序,並使用正則表達式對程序輸出做出特定響應,以此實現與其自動交互的 Python 模塊。 Pexpect 的使用范圍很廣,可以用來實現與 ssh、ftp 、telnet 等程序的自動交互;可以用來自動復制軟件安裝包並在不同機器自動安裝;還可以用來實現軟件測試中與命令行交互的自動化。Pexpect僅能在Unix/Linux平台下使用。
1.1 run函數
run(command, timeout=-1, withexitstatus=False, events=None, extra_args=None, logfile=None, cwd=None, env=None)
若不指定withexitstatus參數,則返回command執行后的返回值,若指定withexitstatus為True,則返回一個包含退出狀態的元組。
1 >>> res = pexpect.run('ls -l /var') 2 >>>(res,exitcode) = pexpect.run(‘ls –l /var’,withexitstatus=True) 3 >>> print(res.replace('\r\n','\n')) 4 總用量 52 5 drwxr-xr-x 2 root root 4096 9月 21 2014 agentx 6 drwxr-xr-x 2 root root 4096 2月 5 2015 backups 7 drwxr-xr-x 21 root root 4096 3月 28 2015 cache 8 drwxr-xr-x 71 root root 4096 3月 28 2015 lib 9 drwxrwsr-x 2 root staff 4096 2月 5 2015 local 10 lrwxrwxrwx 1 root root 9 3月 28 2015 lock -> /run/lock 11 drwxr-xr-x 21 root root 4096 7月 12 21:36 log 12 drwxrwsr-x 2 root mail 4096 2月 6 2015 mail 13 drwxr-xr-x 2 root root 4096 2月 6 2015 opt 14 lrwxrwxrwx 1 root root 4 3月 28 2015 run -> /run 15 drwxr-xr-x 6 root root 4096 3月 28 2015 spool 16 drwxrwxrwt 2 root root 4096 7月 21 20:33 tmp 17 drwxr-xr-x 2 root root 4096 6月 27 2013 unicornscan 18 drwxr-xr-x 2 root root 4096 7月 11 2015 workspace 19 drwxr-xr-x 2 root root 4096 3月 28 2015 www 20 >>>print(exitcode) 21 0
2. spawn類
__init__(self, command, args=[], timeout=30, maxread=2000, searchwindowsize=None, logfile=None, cwd=None, env=None)
This is the constructor. The command parameter may be a string that includes a command and any arguments to the command. For example::
child = pexpect.spawn ('/usr/bin/ftp')
child = pexpect.spawn ('/usr/bin/ssh user@example.com')
child = pexpect.spawn ('ls -latr /tmp')
You may also construct it with a list of arguments like so::
child = pexpect.spawn ('/usr/bin/ftp', [])
child = pexpect.spawn ('/usr/bin/ssh', ['user@example.com'])
child = pexpect.spawn ('ls', ['-latr', '/tmp'])
1 >>>child = pexpect.spawn('ls –l /var') 2 >>> child = pexpect.spawn('ls',['-l','/var']) 3 >>>res = child.readlines() 4 >>> for line in res: 5 ... print(line.strip('\r\n')) 6 ... 7 總用量 52 8 drwxr-xr-x 2 root root 4096 9月 21 2014 agentx 9 drwxr-xr-x 2 root root 4096 2月 5 2015 backups 10 drwxr-xr-x 21 root root 4096 3月 28 2015 cache 11 drwxr-xr-x 71 root root 4096 3月 28 2015 lib 12 drwxrwsr-x 2 root staff 4096 2月 5 2015 local 13 lrwxrwxrwx 1 root root 9 3月 28 2015 lock -> /run/lock 14 drwxr-xr-x 21 root root 4096 7月 12 21:36 log 15 drwxrwsr-x 2 root mail 4096 2月 6 2015 mail 16 drwxr-xr-x 2 root root 4096 2月 6 2015 opt 17 lrwxrwxrwx 1 root root 4 3月 28 2015 run -> /run 18 drwxr-xr-x 6 root root 4096 3月 28 2015 spool 19 drwxrwxrwt 2 root root 4096 7月 21 20:33 tmp 20 drwxr-xr-x 2 root root 4096 6月 27 2013 unicornscan 21 drwxr-xr-x 2 root root 4096 7月 11 2015 workspace 22 drwxr-xr-x 2 root root 4096 3月 28 2015 www
3. spawn實例child的方法
expect(self, pattern, timeout=-1, searchwindowsize=None)
在參數中: pattern 可以是正則表達式, pexpect.EOF , pexpect.TIMEOUT ,或者由這些元素組成的列表。需要注意的是,當 pattern 的類型是一個列表時,且子程序輸出結果中不止一個被匹配成功,則匹配返回的結果是緩沖區中最先出現的那個元素,或者是列表中最左邊的元素。使用 timeout 可以指定等待結果的超時時間 ,該時間以秒為單位。當超過預訂時間時, expect 匹配到pexpect.TIMEOUT。
expect() 在執行中可能會拋出兩種類型的異常分別是 EOF and TIMEOUF,其中 EOF 通常代表子程序的退出, TIMEOUT 代表在等待目標正則表達式中出現了超時。
1 try: 2 index = child.expect (['good', 'bad']) 3 if index == 0: 4 do_something() 5 elif index == 1: 6 do_something_else() 7 except EOF: 8 do_some_other_thing() 9 except TIMEOUT: 10 do_something_completely_different()
expect 不斷從讀入緩沖區中匹配目標正則表達式,當匹配結束時 pexpect 的 before 成員中保存了緩沖區中匹配成功處之前的內容, pexpect 的 after 成員保存的是緩沖區中與目標正則表達式相匹配的內容。
1 >>> child = pexpect.spawn('ls -l /var') 2 >>> child.expect(pexpect.EOF) 3 0 4 >>> print child.before 5 總用量 52 6 drwxr-xr-x 2 root root 4096 9月 21 2014 agentx 7 drwxr-xr-x 2 root root 4096 2月 5 2015 backups 8 drwxr-xr-x 21 root root 4096 3月 28 2015 cache 9 drwxr-xr-x 71 root root 4096 3月 28 2015 lib 10 drwxrwsr-x 2 root staff 4096 2月 5 2015 local 11 lrwxrwxrwx 1 root root 9 3月 28 2015 lock -> /run/lock 12 drwxr-xr-x 21 root root 4096 7月 12 21:36 log 13 drwxrwsr-x 2 root mail 4096 2月 6 2015 mail 14 drwxr-xr-x 2 root root 4096 2月 6 2015 opt 15 lrwxrwxrwx 1 root root 4 3月 28 2015 run -> /run 16 drwxr-xr-x 6 root root 4096 3月 28 2015 spool 17 drwxrwxrwt 2 root root 4096 7月 21 20:33 tmp 18 drwxr-xr-x 2 root root 4096 6月 27 2013 unicornscan 19 drwxr-xr-x 2 root root 4096 7月 11 2015 workspace 20 drwxr-xr-x 2 root root 4096 3月 28 2015 www 21 22 >>> print child.after 23 <class 'pexpect.EOF'> 24 25 send(self, s) 26 sendline(self, s='') 27 sendcontrol(self, char)
這些方法用來向子程序發送命令,模擬輸入命令的行為。 與 send() 不同的是 sendline() 會額外輸入一個回車符 ,更加適合用來模擬對子程序進行輸入命令的操作。 當需要模擬發送 “Ctrl+c” 的行為時,還可以使用 sendcontrol() 發送控制字符。
child.sendcontrol('c') #發送crtl + c
由於 send() 系列函數向子程序發送的命令會在終端顯示,所以也會在子程序的輸入緩沖區中出現,因此不建議使用 expect 匹配最近一次 sendline() 中包含的字符。否則可能會在造成不希望的匹配結果。
4. ftp交互實例
1 >>> import pexpect 2 >>> child = pexpect.spawn('ftp 192.168.1.102') 3 >>> child.expect('Name.*') 4 0 #只匹配一個正則‘Name.*’,因此返回0 5 >>> child.sendline('ftp_admin') 6 10 7 >>> child.expect('Password:') 8 0 9 >>> child.sendline('password') 10 9 11 >>> child.expect('ftp>') 12 0 13 >>> child.sendline('get NmapScanner.py') 14 19
5. pxssh類的使用
Pxssh 做為 pexpect 的派生類可以用來建立一個 ssh 連接,它相比其基類增加了如下方法:
login() 建立到目標機器的ssh連接 ;
losuckgout() 釋放該連接 ;
prompt() 等待提示符,通常用於等待命令執行結束。
1 >>> import pxssh 2 >>> s = pxssh.pxssh() 3 >>> s.login('xx.xx.xx.xx','root','xxxx') 4 True 5 >>> s.sendline('uptime') 6 7 7 >>> print s.before 8 unset PROMPT_COMMAND 9 [root@iZ2594ysug5Z ~]# PS1='[PEXPECT]\$ ' 10 11 >>> s.prompt() 12 True 13 >>> print s.before 14 uptime 15 11:24:34 up 8 days, 21:02, 2 users, load average: 0.00, 0.00, 0.00 16 17 >>> s.logout()
在使用 expect() 時,由於 Pexpect 是不斷從緩沖區中匹配,如果想匹配行尾不能使用 “$” ,只能使用 “\r\n”代表一行的結束。 另外其只能得到最小匹配的結果,而不是進行貪婪匹配,例如 child.expect ('.+') 只能匹配到一個字符。
6. logfile
logfile:
只能通過 spawn 類的構造函數指定。在 spawn 類的構造函數通過參數指定 logfile 時,表示開啟或關閉 logging 。所有的子程序的 input 和 output 都會被 copy 到指定的 logfile 中。設置 logfile 為 None 表示停止 logging,默認就是停止 logging 。設置 logfile 為 sys.stdout,會將所有東西 echo 到標准輸出。
logfile_read和logfile_send:
logfile_read:只用來記錄 python 主程序接收到 child 子程序的輸出,有的時候你不想看到寫給 child 的所有東西,只希望看到 child 發回來的東西。 logfile_send:只用來記錄 python 主程序發送給 child 子程序的輸入 logfile、logfile_read 和 logfile_send 何時被寫入呢? logfile、logfile_read 和 logfile_send 會在每次寫 write 和 send 操作后被 flush 。
調用 send 后,才會往 logfile 和 logfile_send 中寫入,sendline/sendcontrol/sendoff/write/writeline 最終都會調用 send,所以 sendline 后 logfile 中一定有內容了,只要此時 logfile 沒有被 close 。
調用 read_nonblocking 后,才會往 logfile 和 logfile_read 中寫入,expect_loop 會調用 read_nonblocking,而 expect_exact 和 expect_list 都會調用 expect_loop,expect 會調用 expect_list,所以 expect 后 logfile 中一定有內容了,只要此時 logfile 沒有被 close 。