python pexpect模塊的使用


Pexpect 是一個用來啟動子程序並對其進行自動控制的 Python 模塊。 Pexpect 可以用來和像 ssh、ftp、passwd、telnet 等命令行程序進行自動交互。

pexpect模塊在windows功能有限,在linux系統中用得比較多。

pexpect有兩個類或方法用得比較多,一個是pexpect.run() 和pexpect.spawn()

pexpect.run()類似於os.system(),但pexpect.run()會返回執行結果

pexpect.spawn()這個類的功能比較強大,可以用於以下功能:

  • 1. ftp 的使用(注:spawn、expect 和 sendline 的使用)
  • 2. 記錄 log(注:logfile、logfile_sendlogfile_read的使用)
  • 3. ssh 的使用
  • 4. pxssh 的使用
  • 5. telnet 的使用(注:interact 的使用)

使用注意事項:

1.不能使用linux命令中的管道符重定向符等

2.spawn里面的參數列表里面不要使用變量名

以下是實例:

1.pexpect連接ftp:

#!/usr/bin/env python

import pexpect
# 即將 ftp 所要登錄的遠程主機的域名
ipAddress = 'develperWorks.ibm.com'
# 登錄用戶名
loginName = 'root'
# 用戶名密碼
loginPassword = 'passw0rd'

# 拼湊 ftp 命令
cmd = 'ftp ' + ipAddress
# 利用 ftp 命令作為 spawn 類構造函數的參數,生成一個 spawn 類的對象
child = pexpect.spawn(cmd)
# 期望具有提示輸入用戶名的字符出現
index = child.expect(["(?i)name", "(?i)Unknown host", pexpect.EOF, pexpect.TIMEOUT])
# 匹配到了 "(?i)name",表明接下來要輸入用戶名
if ( index == 0 ):
    # 發送登錄用戶名 + 換行符給子程序.
    child.sendline(loginName)
    # 期望 "(?i)password" 具有提示輸入密碼的字符出現.
    index = child.expect(["(?i)password", pexpect.EOF, pexpect.TIMEOUT])
    # 匹配到了 pexpect.EOF 或 pexpect.TIMEOUT,表示超時或者 EOF,程序打印提示信息並退出.
    if (index != 0):
        print "ftp login failed"
        child.close(force=True)
    # 匹配到了密碼提示符,發送密碼 + 換行符給子程序.
    child.sendline(loginPassword)
    # 期望登錄成功后,提示符 "ftp>" 字符出現.
    index = child.expect( ['ftp>', 'Login incorrect', 'Service not available',
    pexpect.EOF, pexpect.TIMEOUT])
    # 匹配到了 'ftp>',登錄成功.
    if (index == 0):
        print 'Congratulations! ftp login correct!'
        # 發送 'bin'+ 換行符給子程序,表示接下來使用二進制模式來傳輸文件.
        child.sendline("bin")
        print 'getting a file...'
        # 向子程序發送下載文件 rmall 的命令.
        child.sendline("get rmall")
        # 期望下載成功后,出現 'Transfer complete.*ftp>',其實下載成功后,
        # 會出現以下類似於以下的提示信息:
        #    200 PORT command successful.
        #    150 Opening data connection for rmall (548 bytes).
        #    226 Transfer complete.
        #    548 bytes received in 0.00019 seconds (2.8e+03 Kbytes/s)
        # 所以直接用正則表達式 '.*' 將 'Transfer complete' 和提示符 'ftp>' 之間的字符全省去.
        index = child.expect( ['Transfer complete.*ftp>', pexpect.EOF, pexpect.TIMEOUT] )
        # 匹配到了 pexpect.EOF 或 pexpect.TIMEOUT,表示超時或者 EOF,程序打印提示信息並退出.
        if (index != 0):
            print "failed to get the file"
            child.close(force=True)
        # 匹配到了 'Transfer complete.*ftp>',表明下載文件成功,打印成功信息,並輸入 'bye',結束 ftp session.
        print 'successfully received the file'
        child.sendline("bye")
    # 用戶名或密碼不對,會先出現 'Login incorrect',然后仍會出現 'ftp>',但是 pexpect 是最小匹配,不是貪婪匹配,
    # 所以如果用戶名或密碼不對,會匹配到 'Login incorrect',而不是 'ftp>',然后程序打印提示信息並退出.
    elif (index == 1):
        print "You entered an invalid login name or password. Program quits!"
        child.close(force=True)
    # 匹配到了 'Service not available',一般表明 421 Service not available, remote server has
    # closed connection,程序打印提示信息並退出.
    # 匹配到了 pexpect.EOF 或 pexpect.TIMEOUT,表示超時或者 EOF,程序打印提示信息並退出.
    else:
        print "ftp login failed! index = " + index
        child.close(force=True)


# 匹配到了 "(?i)Unknown host",表示 server 地址不對,程序打印提示信息並退出
elif index == 1 :
    print "ftp login failed, due to unknown host"
    child.close(force=True)
# 匹配到了 pexpect.EOF 或 pexpect.TIMEOUT,表示超時或者 EOF,程序打印提示信息並退出
else:
    print "ftp login failed, due to TIMEOUT or EOF"
    child.close(force=True)

2.記錄log的日志代碼
#!/usr/bin/env python
"""
This run a user specified command and log its result.

./command.py [-a] [-c command] {logfilename}

logfilename : This is the name of the log file. Default is command.log.
-a : Append to log file. Default is to overwrite log file.
-c : spawn command. Default is the command 'ls -l'.

Example:

This will execute the command 'pwd' and append to the log named my_session.log:

./command.py -a -c 'pwd' my_session.log

"""
import os, sys, getopt
import traceback
import pexpect

# 如果程序中間出錯,打印提示信息后退出
def exit_with_usage():
    print globals()['__doc__']
    os._exit(1)

def main():
    ######################################################################
    # Parse the options, arguments, get ready, etc.
    ######################################################################
    try:
        optlist, args = getopt.getopt(sys.argv[1:], 'h?ac:', ['help','h','?'])
    # 如果指定的參數不是’ -a ’ , ‘ -h ’ , ‘ -c ’ , ‘ -? ’ , ‘ --help ’ ,
    #‘ --h ’或’ --? ’時,會拋出 exception,
    # 這里 catch 住,然后打印出 exception 的信息,並輸出 usage 提示信息.
    except Exception, e:
        print str(e)
        exit_with_usage()
    options = dict(optlist)
    # 最多只能指定一個 logfile,否則出錯.
    if len(args) > 1:
        exit_with_usage()
    # 如果指定的是 '-h','--h','-?','--?' 或 '--help',只輸出 usage 提示信息.
    if [elem for elem in options if elem in ['-h','--h','-?','--?','--help']]:
        print "Help:"
        exit_with_usage()
    # 獲取 logfile 的名字.
    if len(args) == 1:
        script_filename = args[0]
    else:
    # 如果用戶沒指定,默認 logfile 的名字是 command.log
        script_filename = "command.log"
    # 如果用戶指定了參數 -a,如果之前該 logfile 存在,那么接下來的內容會附加在原先內容之后,
    # 如果之前沒有該  logfile,新建一個文件,並且接下來將內容寫入到該文件中.
    if '-a' in options:
        fout = open (script_filename, "ab")
    else:
    # 如果用戶沒指定參數 -a,默認按照用戶指定 logfile 文件名新建一個文件,然后將接下來將內容寫入到該文件中.
        fout = open (script_filename, "wb")
    # 如果用戶指定了 -c 參數,那么運行用戶指定的命令.
    if '-c' in options:
        command = options['-c']
    # 如果用戶沒有指定 -c 參數,那么默認運行命令'ls – l'
    else:
        command = "ls -l"

    # logfile 文件的 title
    fout.write ('==========Log Tile: IBM developerWorks China==========\n')

    # 為接下來的運行命令生成一個 pexpect 的 spawn 類子程序的對象.
    p = pexpect.spawn(command)
    # 將之前 open 的 file 對象指定為 spawn 類子程序對象的 log 文件.
    p.logfile = fout
    # 命令運行完后,expect EOF 出現,這時會將 spawn 類子程序對象的輸出寫入到 log 文件.
    p.expect(pexpect.EOF)
    #open 完文件,使用完畢后,需關閉該文件.
    fout.close()
    return 0

if __name__ == "__main__":
    try:
        main()
    except SystemExit, e:
        raise e
    except Exception, e:
        print "ERROR"
        print str(e)
        traceback.print_exc()
        os._exit(1)

注:

  • 運行:./command.py -a -c who cmd.log
運行結束后,cmd.log 的內容為:
IBM developerWorks China
Root 	 :0 		 2009-05-12 22:40
Root 	 pts/1 		 2009-05-12 22:40 (:0.0)
Root 	 pts/2 		 2009-07-05 18:55 (9.77.180.94)

 

  • logfile

只能通過 spawn 類的構造函數指定。在 spawn 類的構造函數通過參數指定 logfile 時,表示開啟或關閉 logging 。所有的子程序的 input 和 output 都會被 copy 到指定的 logfile 中。設置 logfile 為 None 表示停止 logging,默認就是停止 logging 。設置 logfile 為 sys.stdout,會將所有東西 echo 到標准輸出。

  • logfile_readlogfile_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 。
  • 如果調用的函數最終都沒有調用 send 或 read_nonblocking,那么 logfile 雖然被分配指定了一個 file,但其最終結果是:內容為空。見下例:

 

清單 3. log 內容為空的例子代碼

import pexpect
p = pexpect.spawn( ‘ ls -l ’ )
fout = open ('log.txt', "w")
p.logfile = fout
fout.close()

 

運行該腳本后,你會發現其實 log.txt 是空的,沒有記錄 ls -l 命令的內容,原因是沒有調用 send 或 read_nonblocking,真正的內容沒有被 flush 到 log 中。如果在 fout.close() 之前加上 p.expect(pexpect.EOF),log.txt 才會有 ls -l 命令的內容。

 

3.ssh的使用

#!/usr/bin/env python

"""
This runs a command on a remote host using SSH. At the prompts enter hostname,
user, password and the command.
"""

import pexpect
import getpass, os

#user: ssh 主機的用戶名
#host:ssh 主機的域名
#password:ssh 主機的密碼
#command:即將在遠端 ssh 主機上運行的命令
def ssh_command (user, host, password, command):
    """
    This runs a command on the remote host. This could also be done with the
    pxssh class, but this demonstrates what that class does at a simpler level.
    This returns a pexpect.spawn object. This handles the case when you try to
    connect to a new host and ssh asks you if you want to accept the public key
    fingerprint and continue connecting.
    """
    ssh_newkey = 'Are you sure you want to continue connecting'
    # 為 ssh 命令生成一個 spawn 類的子程序對象.
    child = pexpect.spawn('ssh -l %s %s %s'%(user, host, command))
    i = child.expect([pexpect.TIMEOUT, ssh_newkey, 'password: '])
    # 如果登錄超時,打印出錯信息,並退出.
    if i == 0: # Timeout
        print 'ERROR!'
        print 'SSH could not login. Here is what SSH said:'
        print child.before, child.after
        return None
    # 如果 ssh 沒有 public key,接受它.
    if i == 1: # SSH does not have the public key. Just accept it.
        child.sendline ('yes')
        child.expect ('password: ')
        i = child.expect([pexpect.TIMEOUT, 'password: '])
        if i == 0: # Timeout
        print 'ERROR!'
        print 'SSH could not login. Here is what SSH said:'
        print child.before, child.after
        return None
    # 輸入密碼.
    child.sendline(password)
    return child

def main ():
    # 獲得用戶指定 ssh 主機域名.
    host = raw_input('Hostname: ')
    # 獲得用戶指定 ssh 主機用戶名.
    user = raw_input('User: ')
    # 獲得用戶指定 ssh 主機密碼.
    password = getpass.getpass()
    # 獲得用戶指定 ssh 主機上即將運行的命令.
    command = raw_input('Enter the command: ')
    child = ssh_command (user, host, password, command)
    # 匹配 pexpect.EOF
    child.expect(pexpect.EOF)
    # 輸出命令結果.
    print child.before

if __name__ == '__main__':
    try:
        main()
    except Exception, e:
        print str(e)
        traceback.print_exc()
        os._exit(1)


4. pxssh的使用

 

#!/usr/bin/env python
import pxssh
import getpass
try:
    # 調用構造函數,創建一個 pxssh 類的對象.
    s = pxssh.pxssh()
    # 獲得用戶指定 ssh 主機域名.
    hostname = raw_input('hostname: ')
    # 獲得用戶指定 ssh 主機用戶名.
    username = raw_input('username: ')
    # 獲得用戶指定 ssh 主機密碼.
    password = getpass.getpass('password: ')
    # 利用 pxssh 類的 login 方法進行 ssh 登錄,原始 prompt 為'$' , '#'或'>'
    s.login (hostname, username, password, original_prompt='[$#>]')
    # 發送命令 'uptime'
    s.sendline ('uptime')
    # 匹配 prompt
    s.prompt()
    # 將 prompt 前所有內容打印出,即命令 'uptime' 的執行結果.
    print s.before
    # 發送命令 ' ls -l '
    s.sendline ('ls -l')
    # 匹配 prompt
    s.prompt()
    # 將 prompt 前所有內容打印出,即命令 ' ls -l ' 的執行結果.
    print s.before
    # 退出 ssh session
    s.logout()
except pxssh.ExceptionPxssh, e:
    print "pxssh failed on login."
    print str(e)

5.telnet 的使用

#!/usr/bin/env python
import pexpect

# 即將 telnet 所要登錄的遠程主機的域名
ipAddress = 'develperWorks.ibm.com'
# 登錄用戶名
loginName = 'root'
# 用戶名密碼
loginPassword = 'passw0rd'
# 提示符,可能是’ $ ’ , ‘ # ’或’ > ’
loginprompt = '[$#>]'

# 拼湊 telnet 命令
cmd = 'telnet ' + ipAddress
# 為 telnet 生成 spawn 類子程序
child = pexpect.spawn(cmd)
# 期待'login'字符串出現,從而接下來可以輸入用戶名
index = child.expect(["login", "(?i)Unknown host", pexpect.EOF, pexpect.TIMEOUT])
if ( index == 0 ):
    # 匹配'login'字符串成功,輸入用戶名.
    child.sendline(loginName)
    # 期待 "[pP]assword" 出現.
    index = child.expect(["[pP]assword", pexpect.EOF, pexpect.TIMEOUT])
    # 匹配 "[pP]assword" 字符串成功,輸入密碼.
    child.sendline(loginPassword)
    # 期待提示符出現.
    child.expect(loginprompt)
    if (index == 0):
        # 匹配提示符成功,輸入執行命令 'ls -l'
        child.sendline('ls -l')
        # 立馬匹配 'ls -l',目的是為了清除剛剛被 echo 回顯的命令.
        child.expect('ls -l')
        # 期待提示符出現.
        child.expect(loginprompt)
        # 將 'ls -l' 的命令結果輸出.
        print child.before
        print "Script recording started. Type ^] (ASCII 29) to escape from the script 
              shell."
        # 將 telnet 子程序的執行權交給用戶.
        child.interact()
        print 'Left interactve mode.'
    else:
        # 匹配到了 pexpect.EOF 或 pexpect.TIMEOUT,表示超時或者 EOF,程序打印提示信息並退出.
        print "telnet login failed, due to TIMEOUT or EOF"
        child.close(force=True)
else:
    # 匹配到了 pexpect.EOF 或 pexpect.TIMEOUT,表示超時或者 EOF,程序打印提示信息並退出.
    print "telnet login failed, due to TIMEOUT or EOF"
    child.close(force=True)


更詳細的情況請查看:
https://www.cnblogs.com/mmdln/p/9006274.html

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM