首页 > 解决方案 > 如何从 pexpect.spawn() 收集所有输出

问题描述

目前,c.read() 函数仅从我正在调用的程序的输出中返回值,并且不收集在交互部分发生的情况。(换句话说:等待用户名和密码)。由于该部分可能发生错误,因此我需要在调用 cmd 后收集所有输出。

使用运行命令工作正常。我正在尝试转换为使用 expect.spawn(),因为它看起来非常接近 ssh 版本的 expect。当前 run() 方法可以正常工作,因为它可以在本地工作。我找不到可以返回整个输出的 getOutput() 类型函数。

import pexpect
class Expect( ):
    def Do( self, cmd, program: list = [], timeout: int = 20 ):
        # result = run( cmd, events=program, timeout=timeout).decode() 

        result = ''
        c = pexpect.spawn( cmd, encoding='utf-8', timeout=timeout )
        for curItem in program:
            # print( 'wait: ' + curItem[0] + ' resp: ' + curItem[1])
            c.expect( curItem[0] )
            c.sendline( curItem[1] )
            # result += c.read()
    
        result += c.read()

        print ( 'res: ' + str(result) )
        return result


cmd  = 'fmsadmin list files -s'
prog = [('username \\(.+\\):', 'yourUN\n'), ('password:', 'yourPW\n')]
res  = Expect().Do( cmd, prog )    # Returns everything
# use results to verify functionality

结果

ID File                  Clients Size   Status Enabled Extended Privileges                                                                   Encrypted 
1  FMServer_Sample.fmp12 0       905216 Normal fmapp fmxml fmphp fmwebdirect                                                                 No        

当我期待

username (yourUN):yourUN
password:
ID File                  Clients Size   Status Enabled Extended Privileges                                                                   Encrypted 
1  FMServer_Sample.fmp12 0       905216 Normal fmapp fmxml fmphp fmwebdirect                                                                 No        

更新

我在 c.sendline( curItem[1] ) 下添加了 result += c.before + c.after 现在返回: c.sendline( curItem[1] )

username (yourUN): yourUN

password:
ID File                  Clients Size   Status Enabled Extended Privileges                                                                   Encrypted 
1  FMServer_Sample.fmp12 0       905216 Normal fmapp fmxml fmphp fmwebdirect                                                                 No        

不幸的是,仍然存在两个问题。1 还有其他值,例如输出中的返回值,如果我提供无效密码,我会在没有应用程序返回值的情况下返回调试详细信息。我得到:

before (last 100 chars): '\r\n/usr/local/bin/fmsadmin: Permission denied, please try again.\r\nusername (yourUN):'
after: <class 'pexpect.exceptions.TIMEOUT'>
match: None
match_index: None
etc

所以简而言之,我正在寻找来自 CLI 的完整返回值,就像我手动完成时返回的一样。这就是 run() 的作用。

标签: python-3.xpexpect

解决方案


Pexpect 就像最初的 TCL Expect 一样,很难获得会话输入和输出的正确部分。挑战在于格式化您的搜索模式以匹配它应该匹配的内容,并且仅此而已。要使其正常工作,您所要做的就是用,和属性解析返回的对象。beforeaftermatch

也就是说,Pexpect 获得了终端显示的所有内容,包括命令和输出。幸运的是,密码没有显示出来,不幸的是,Pexpect 看不到它们。

from pexpect import spawn, EOF  # Better import only needed methods
process = spawn('bash', encoding='utf-8')
process.expect('$')             # Expects terminal prompt

process.sendline('read -p "username: " var')
process.expect('\nusername: ')  # Gets command, newline and username prompt
process.sendline('me')
process.expect('[^\n]*\n.*$')   # Gets typed string, newlind and terminal prompt
print(process.match.group(0))   # Prints whole string matched
me                              # User typed
me@myhost ~ $                   # Terminal prompt

process.sendline('read -sp "password: " var')   # Note the '-s' for 'silent'
process.expect('\npassword: ')
process.sendline('my_passwd')
process.expect('.*$')           # Trying to match a newline just doesn't work
print(process.match.group(0))
me@myhost ~ $                   # Only terminal prompt

process.sendline('exit')
process.expect(EOF)

所以你应该知道你想解决什么样的问题。你为什么要收集所有发送和返回的东西,包括密码?

顺便说一句,您可能已经知道代码中的纯密码问题。我的建议getpass::

from getpass import getpass
password = getpass()
<Then use the password>

BTW 2,以防万一,有文档


推荐阅读