如何使用Python调用命令,捕获stderr和stdout,而无需等待stderr / stdout关闭?

时间:2014-01-15 16:58:21

标签: python subprocess

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

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

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

  

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

显然,我也想避免陷入僵局。

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

2 个答案:

答案 0 :(得分: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)
""")]

答案 1 :(得分:0)

我尝试过一个简单的例子。到目前为止我所拥有的只是捕获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 example2.py 我有实时输出(我认为这是真的:)):

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

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