简体   繁体   English

Pexpect多线Output

[英]Pexpect Mulitple Line Output

Question问题

How do you get the output of a command with multiple lines of output using pexpect?如何使用 pexpect 获得具有多行 output 的命令的 output?

Example例子

This code works, albeit with the output smashed into one line:此代码有效,尽管 output 被砸成一行:

child = pexpect.spawn('ping -c 3 1.1.1.1')
child.expect(pexpect.EOF)
print(child.before)

However, this code does not work:但是,此代码不起作用:

child = pexpect.spawn('hostname')
child.expect(pexpect.EOF)
print(child.before)

child.seldline('ping -c 3 1.1.1.1')
child.expect(pexpect.EOF)
print(child.before)

How would I get this second code to work?我将如何让第二个代码工作?

Background背景

I have commands that I need to run to get connected (replaced here with hostname) and then commands that output mulitiple lines (replaced here with ping) that I cannot seem to get the output from.我有我需要运行才能连接的命令(此处替换为主机名),然后是 output 多行命令(此处替换为 ping),我似乎无法从中获取 output。 If I look for any string other than EOF, I get an EOF exception...如果我查找 EOF 以外的任何字符串,我会收到 EOF 异常...

The commands I am actually running are here if you need proof:如果您需要证明,我实际运行的命令在这里:

The answer in this other question may be deprecated because this section of code copied exactly just outputs b'' over and over again.这个其他问题的答案可能会被弃用,因为这部分代码完全复制只是一遍又一遍地输出b''

By default, the data is of bytes , not str .默认情况下,数据是bytes ,而不是str This is why "the output smashed into one line".这就是为什么“output砸成一行”的原因。 See the following example:请参见以下示例:

$ cat foo.py
import pexpect

child = pexpect.spawn('ping -c 2 127.0.0.1')
child.expect(pexpect.EOF)
print(child.before)


$ python3 foo.py
b'PING 127.0.0.1 (127.0.0.1) 56(84) bytes of data.\r\n64 bytes from 127.0.0.1: icmp_seq=1 ttl=64 time=0.027 ms\r\n64 bytes from 127.0.0.1: icmp_seq=2 ttl=64 time=0.056 ms\r\n\r\n--- 127.0.0.1 ping statistics ---\r\n2 packets transmitted, 2 received, 0% packet loss, time 30ms\r\nrtt min/avg/max/mdev = 0.027/0.041/0.056/0.015 ms\r\n'
$

You can decode() when printing:打印时可以decode()

$ cat foo.py
import pexpect

child = pexpect.spawn('ping -c 2 127.0.0.1')
child.expect(pexpect.EOF)
print(child.before.decode(encoding='utf8') )  # utf8 is the default for bytes.decode()


$ python3 foo.py
PING 127.0.0.1 (127.0.0.1) 56(84) bytes of data.
64 bytes from 127.0.0.1: icmp_seq=1 ttl=64 time=0.024 ms
64 bytes from 127.0.0.1: icmp_seq=2 ttl=64 time=0.054 ms

--- 127.0.0.1 ping statistics ---
2 packets transmitted, 2 received, 0% packet loss, time 11ms
rtt min/avg/max/mdev = 0.024/0.039/0.054/0.015 ms

$

Or you can specify the encoding when calling spawn() :或者您可以在调用spawn()时指定编码:

$ cat foo.py
import pexpect

child = pexpect.spawn('ping -c 2 127.0.0.1', encoding='utf8')
child.expect(pexpect.EOF)
print(child.before)


$ python3 foo.py
PING 127.0.0.1 (127.0.0.1) 56(84) bytes of data.
64 bytes from 127.0.0.1: icmp_seq=1 ttl=64 time=0.036 ms
64 bytes from 127.0.0.1: icmp_seq=2 ttl=64 time=0.053 ms

--- 127.0.0.1 ping statistics ---
2 packets transmitted, 2 received, 0% packet loss, time 20ms
rtt min/avg/max/mdev = 0.036/0.044/0.053/0.010 ms

$

Spawn a PID that will stay open生成一个将保持打开状态的 PID

The real question: Why the second example doesn't work.真正的问题:为什么第二个例子不起作用。

The pexpect.spawn object ('child' here) points to a process id (PID). pexpect.spawn object(此处为“孩子”)指向进程 ID(PID)。 The example I was trying to use was not working because hostname was running and then exiting.我尝试使用的示例不起作用,因为hostname正在运行然后退出。 In my real usecase, I was using ssh and then several other necessary steps before the long output command (represented by ping here).在我的实际用例中,我使用ssh ,然后在长 output 命令之前使用了其他几个必要步骤(在此处由ping表示)。

Starting your multi-step process with a command that keeps running will solve that issue.使用持续运行的命令启动多步骤流程将解决该问题。 Either of these Examples will work:这些示例中的任何一个都可以使用:

child = pexpect.spawn('ssh user@host')
child = pexpect.spawn('bin/bash')

I switched to the latter which spins up a new shell that I can interact with.我切换到后者,它启动了一个我可以与之交互的新 shell。 This allowed me to add some error handling to the ssh connection and reuse the code several times within the one shell.这使我可以向 ssh 连接添加一些错误处理,并在一个 shell 中多次重用代码。

Note that if you exit the ssh connection or bash shell respectively, you will need to spawn a new 'child' to send more commands.请注意,如果您分别退出 ssh 连接或 bash shell 连接,则需要生成一个新的“孩子”来发送更多命令。

Use a non-blocking read使用非阻塞读取

Extra Detail: Fixing the first/working example's output.额外细节:修复第一个/工作示例的 output。

A this code will return the output of the last command without changing it. A 此代码将返回最后一个命令的 output 而不会更改它。

def try_read(child):
    """Based on pexpect.pxssh.try_read_prompt"""
    total_timeout = 3
    timeout = 0.5
    inter_char_timeout = 0.1
    begin = time.time()
    expired = 0
    prompt = ''
    while expired < total_timeout:
        try:
            prompt += child.read_nonblocking(size=1, timeout=timeout)
            expired = time.time() - begin # updated total time expired
            timeout = inter_char_timeout
        except TimeoutError:
            print("read ended with TimeoutError")
            break
        except pexpect.TIMEOUT:
            print("read ended with pexpect.Timeout")
            break
        except pexpect.EOF:
            print("read ended with pexpect.EOF")
            break
    return prompt

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM