【问题标题】:How can I call a command using Python, capturing stderr and stdout, without waiting for stderr/stdout to be closed?如何使用 Python 调用命令,捕获 stderr 和 stdout,而无需等待 stderr/stdout 关闭?
【发布时间】:2014-01-15 16:58:20
【问题描述】:

现在我有一些代码使用子进程中的Popen.communicate()(设置stdin=PIPEstderr=PIPE)来运行命令并捕获stderr 和stdout。

问题是communicate()不仅等待命令退出,还等待stdout和stderr关闭。我正在运行的命令会生成一个子进程,它使 stderr 保持打开状态,因此即使命令已完成运行(并在 ps 中显示为“已失效”)communicate() 仍然挂起。

我只想等待命令完成而不等待 stderr/stdout。但我仍然想捕获命令运行时给出的任何 stderr/stdout 输出。 The documentation for wait() 附有一个带有免责声明的红框:

这将在使用 stdout=PIPE 和/或 stderr=PIPE 时发生死锁,并且子进程会向管道生成足够的输出,从而阻塞等待 OS 管道缓冲区接受更多数据。使用communicate() 来避免这种情况。

显然,我也想避免死锁。

完成这项任务的正确方法是什么?

【问题讨论】:

标签: python subprocess


【解决方案1】:

“在 ps 中显示为“defunct”” 意味着您可能在 selectfcntl 可以工作的系统上,即您可以轻松读取 stdout/stderr 而不会阻塞。

示例:A 启动 B(cmd,子),B 启动 C(孙),A 读取输出直到 B 退出或 EOF:

#!/usr/bin/env python
import os
from select import select
from subprocess import Popen, PIPE

p = Popen(cmd, stdout=PIPE, stderr=PIPE, bufsize=0)
read_set = [p.stdout, p.stderr]
pipename = {p.stdout: "stdout", p.stderr: "stderr"}
timeout = 0.5 # ugly but it works
while read_set and p.poll() is None: # while subprocess is running or until EOF
    for pipe in select(read_set, [], [], timeout)[0]:
        data = os.read(pipe.fileno(), 1<<30)
        if data:
            print("got from %s: %r" % (pipename[pipe], data))
        else: # EOF
            pipe.close()
            read_set.remove(pipe)
print("exit code %s" % (p.wait(),))

# child exited, wait for grandchild to print
for pipe in read_set:
    print("read the rest of %s: %r" % (pipename[pipe], pipe.read()))
    pipe.close()

cmd:

import sys
from textwrap import dedent

cmd = [sys.executable, '-u', '-c', dedent("""
    # inception style
    import os
    import sys
    from subprocess import Popen
    from textwrap import dedent

    Popen([sys.executable, '-u', '-c', dedent('''
        import os
        import sys
        import time

        time.sleep(60)
        print("grandchild %d done" % os.getpid())
        sys.stderr.write("grandchild stderr")
        sys.exit(20)
    ''')]) # stdout/stderr are not redirected

    print('child %d done' % os.getpid())
    sys.stderr.write('child stderr')
    sys.exit(19)
""")]

【讨论】:

    【解决方案2】:

    我尝试了一个简单的例子。到目前为止,我所得到的只是捕获我创建的两个文件的 std.err。第一个的内容是(example1.py):

    import time
    import sys
    
    for i in xrange(1000):
        print >>sys.stderr, "hello", i
        time.sleep(1)
    

    第二个(example2.py)的内容:

    import subprocess
    
    cmd = "python example1.py"
    
    print subprocess.check_output(cmd, shell=True)
    

    我刚刚调用了 example2.py 脚本:

    python示例2.py 而且我有实时输出(我认为这是真的:)):

    hello 0
    hello 1
    hello 2
    hello 3
    hello 4
    hello 5
    hello 6
    

    我仍然不知道如何处理标准输出,但如果我管理它,我会在这里发布答案

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2012-02-29
      • 2013-01-12
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-09-05
      • 2020-06-23
      相关资源
      最近更新 更多