如何从 pexpect.spawn() 收集所有输出

How do I collect ALL output from pexpect.spawn()

目前 c.read() 函数仅 returns 来自我正在调用的程序输出的值,不收集在交互部分发生的事情。 (:等待用户名和密码)。由于该部分可能会发生错误,因此我需要在调用 cmd 后收集所有输出。

使用 运行 命令可以正常工作。我正在尝试转换为使用 expect.spawn(),因为它看起来非常接近 expect 的 ssh 版本。当前 运行() 方法在本地工作时工作正常。我找不到可以 return 整个输出的 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] ) 下面添加了结果 += c.before + c.after 现在 returns: 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 输出中还有其他值,例如 returns,如果我提供无效密码,我会在没有应用 returned 值的情况下返回调试详细信息。我得到:

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 中寻找完整的 returned 值,就像我手动完成 returned 一样。这就是 运行() 的作用。

Pexpect,就像原来的 TCL Expect 一样,要获得会话输入和输出的正确部分有点棘手。挑战在于格式化您的搜索模式以匹配它应该匹配的内容,并且仅此匹配。要让它正常工作,您所要做的就是用 beforeaftermatch 属性解析 returned object

也就是说,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>

顺便说一句,为了以防万一,docs 左右。

好的,我明白了。不太期望,但它是有道理的。几个变化:

  1. 在 for 循环末尾添加结果 += c.before + c.after。这捕获 来自帐户和密码的输出以及来自 CLI 的任何错误 申请。
  2. 将 spawn、expect、send 放入 try accept 块中
  3. Move result += c.read() inside Try before Except (quiet the 例外)
  4. 异常期间终于调用result += c.buffer

我可能会在下一个版本中删除本地部分(一旦我弄清楚远程案例)

from pexpect import *

class Expect( ):
    def Do( self, cmd, program: list = [], timeout: int = 20 ):
        local   = False
        result  = ''
        if local:
            result = runu( cmd, events=program, timeout=timeout ) 
        else:
            c = ''
            try:
                c = spawnu( cmd, timeout=timeout )
                for curItem in program:
                    c.expect( curItem[0] )
                    c.sendline( curItem[1] )
                    result += c.before + c.after
                result += c.read()
            except:
                result += c.buffer
        return result

cmd  = 'fmsadmin list files -s'
prog = [('username \(.+\):', 'yourUN\n'), ('password:', 'yourPW\n')]
res  = Expect().Do( cmd, prog, 5 )
print ( '-' * 50 + '\n' + str(res))