简体   繁体   English

Python - 捕获Popen stdout并在控制台上显示?

[英]Python - capture Popen stdout AND display on console?

I want to capture stdout from a long-ish running process started via subprocess.Popen(...) so I'm using stdout=PIPE as an arg. 我想从通过subprocess.Popen(...)启动的长时间运行进程捕获stdout所以我使用stdout=PIPE作为arg。

However, because it's a long running process I also want to send the output to the console (as if I hadn't piped it) to give the user of the script an idea that it's still working. 但是,因为它是一个长时间运行的进程,我还想将输出发送到控制台(好像我没有管道它)给脚本用户一个它仍在工作的想法。

Is this at all possible? 这是可能吗?

Cheers. 干杯。

The buffering your long-running sub-process is probably performing will make your console output jerky and very bad UX. 缓存你的长时间运行的子进程可能会使你的控制台输出生涩和非常糟糕的用户体验。 I suggest you consider instead using pexpect (or, on Windows, wexpect ) to defeat such buffering and get smooth, regular output from the sub-process. 我建议你考虑使用pexpect (或者,在Windows上, wexpect )来打败这样的缓冲并从子进程获得平滑,规则的输出。 For example (on just about any unix-y system, after installing pexpect): 例如(在任何unix-y系统上,安装pexpect之后):

>>> import pexpect
>>> child = pexpect.spawn('/bin/bash -c "echo ba; sleep 1; echo bu"', logfile=sys.stdout); x=child.expect(pexpect.EOF); child.close()
ba
bu
>>> child.before
'ba\r\nbu\r\n'

The ba and bu will come with the proper timing (about a second between them). ba和bu将具有适当的时间(它们之间大约一秒)。 Note the output is not subject to normal terminal processing, so the carriage returns are left in there -- you'll need to post-process the string yourself (just a simple .replace !-) if you need \\n as end-of-line markers (the lack of processing is important just in case the sub-process is writing binary data to its stdout -- this ensures all the data's left intact!-). 请注意,输出不受正常的终端处理,因此回车留在那里 - 你需要自己后处理字符串(只是一个简单的.replace ! - ),如果你需要\\n作为结束-line标记(缺少处理非常重要,以防子进程将二进制数据写入其stdout - 这可确保所有数据保持完整! - )。

S. Lott's comment points to Getting realtime output using subprocess and Real-time intercepting of stdout from another process in Python S. Lott的评论指出使用子 进程 获取实时输出在Python中实时拦截来自另一个进程的stdout

I'm curious that Alex's answer here is different from his answer 1085071. My simple little experiments with the answers in the two other referenced questions has given good results... 我很好奇亚历克斯的答案与他的答案1085071不同。我在其他两个引用问题中的答案的简单小实验已经给出了很好的结果......

I went and looked at wexpect as per Alex's answer above, but I have to say reading the comments in the code I was not left a very good feeling about using it. 我按照亚历克斯的回答看了看wexpect,但我不得不说读代码中的注释我对使用它没有很好的感觉。

I guess the meta-question here is when will pexpect/wexpect be one of the Included Batteries? 我想这里的元问题是什么时候pexpect / wexpect会成为包含的电池之一?

您可以在从管道中读取时将其print出来吗?

Alternatively, you can pipe your process into tee and capture only one of the streams. 或者,您可以将流程导入tee并仅捕获其中一个流。 Something along the lines of sh -c 'process interesting stuff' | tee /dev/stderr sh -c 'process interesting stuff' | tee /dev/stderr某些东西sh -c 'process interesting stuff' | tee /dev/stderr sh -c 'process interesting stuff' | tee /dev/stderr . sh -c 'process interesting stuff' | tee /dev/stderr

Of course, this only works on Unix-like systems. 当然,这只适用于类Unix系统。

Inspired by pty.openpty() suggestion somewhere above, tested on python2.6, linux. 灵感来自上面某处的pty.openpty()建议,在python2.6,linux上测试过。 Publishing since it took a while to make this working properly, w/o buffering... 发布以来需要一段时间才能使其正常工作,无需缓冲......

def call_and_peek_output(cmd, shell=False):
    import pty, subprocess
    master, slave = pty.openpty()
    p = subprocess.Popen(cmd, shell=shell, stdin=None, stdout=slave, close_fds=True)
    os.close(slave)
    line = ""
    while True:
        try:
            ch = os.read(master, 1)
        except OSError:
            # We get this exception when the spawn process closes all references to the
            # pty descriptor which we passed him to use for stdout
            # (typically when it and its childs exit)
            break
        line += ch
        sys.stdout.write(ch)
        if ch == '\n':
            yield line
            line = ""
    if line:
        yield line

    ret = p.wait()
    if ret:
        raise subprocess.CalledProcessError(ret, cmd)

for l in call_and_peek_output("ls /", shell=True):
    pass

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

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