<blockquote>
<p>The stdout and stderr of the program being run can be logged separately.</p>
</blockquote>
<p>您不能使用<code>pexpect</code>,因为stdout和stderr都指向同一个<code>pty</code>,之后无法将它们分开。在</p>
<blockquote>
<p>The stdout and stderr of the program being run can be viewed in near-real time, such that if the child process hangs, the user can see. (i.e. we do not wait for execution to complete before printing the stdout/stderr to the user)</p>
</blockquote>
<p>如果子进程的输出不是tty,那么<a href="http://pexpect.readthedocs.org/en/latest/FAQ.html#whynotpipe" rel="noreferrer">it is likely that it uses a block buffering</a>,因此如果它不产生太多的输出,那么<em>就不会是“实时的”</em>,例如,如果缓冲区是4K,则父Python进程将看不到任何内容,直到子进程打印4K字符并缓冲区溢出或显式刷新(在子进程内部)。这个缓冲区在子进程内部,没有从外部管理它的标准方法。下面的图片显示了<code>command 1 | command2</code>shell管道的stdio缓冲区和管道缓冲区:</p>
<p><a href="http://www.pixelbeat.org/programming/stdio_buffering/" rel="noreferrer"><img src="https://i.stack.imgur.com/Y99ip.png" alt="pipe/stdio buffers"/></a></p>
<blockquote>
<p>The program being run does not know it is being run via python, and thus will not do unexpected things (like chunk its output instead of printing it in real-time, or exit because it demands a terminal to view its output).</p>
</blockquote>
<p>看起来,您的意思正好相反,也就是说,如果输出被重定向到管道(当您在Python中使用<code>stdout=PIPE</code>)时,您的子进程很可能会将其输出分块,而不是尽快刷新每个输出行。这意味着默认的<a href="https://stackoverflow.com/a/4985080/4279">threading</a>或<a href="https://stackoverflow.com/a/25960956/4279">asyncio solutions</a>不会像您的情况那样工作。在</p>
<p>有几种解决方法:</p>
<ul>
<li><p>命令可以接受命令行参数,例如<code>grep line-buffered</code>或{<cd6>},以禁用块缓冲。</p></li>
<li><p><a href="https://stackoverflow.com/a/12471855/4279">^{<cd7>} works for some programs</a>即,您可以使用上面的线程或异步解决方案运行<code>['stdbuf', '-oL', '-eL'] + command</code>,您应该分别获得stdout、stderr,并且行应该以接近实时的方式出现:</p>
<pre><code>#!/usr/bin/env python3
import os
import sys
from select import select
from subprocess import Popen, PIPE
with Popen(['stdbuf', '-oL', '-e0', 'curl', 'www.google.com'],
stdout=PIPE, stderr=PIPE) as p:
readable = {
p.stdout.fileno(): sys.stdout.buffer, # log separately
p.stderr.fileno(): sys.stderr.buffer,
}
while readable:
for fd in select(readable, [], [])[0]:
data = os.read(fd, 1024) # read available
if not data: # EOF
del readable[fd]
else:
readable[fd].write(data)
readable[fd].flush()
</code></pre></li>
<li><p>最后,您可以尝试使用两个<code>pty</code>+<code>select</code>解决方案:</p>
^{pr2}$
<p>我不知道对stdout和stderr使用不同的<code>pty</code>有什么副作用。在您的例子中,您可以尝试一个pty是否足够,例如,设置<code>stderr=PIPE</code>,并使用<code>p.stderr.fileno()</code>而不是{<cd15>}。<a href="https://github.com/amoffat/sh/blob/7a4e29655362c86bd3b71c3122a24709b9bcbb10/sh.py#L1194" rel="noreferrer">Comment in ^{<cd16>} source suggests that there are issues if ^{<cd17>}</a></p></li>
</ul>