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

如何使用Python调用命令,捕获stderr和stdout,而不等待stderr/stdout关闭?,python,subprocess,Python,Subprocess,现在我有一些代码使用from子流程(设置stdin=PIPE和stderr=PIPE)来运行命令并捕获stderr和stdout 问题在于communicate()不仅等待命令退出,还等待stdout和stderr关闭。我正在运行的命令生成一个子进程,该子进程使stderr保持打开状态,因此即使该命令已完成运行(并在ps中显示为“defunct”)communicate()仍处于挂起状态 我只想等待命令完成,而不想等待stderr/stdout。但我仍然希望捕获命令运行时给出的任何stderr/

现在我有一些代码使用from子流程(设置
stdin=PIPE
stderr=PIPE
)来运行命令并捕获stderr和stdout

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

我只想等待命令完成,而不想等待stderr/stdout。但我仍然希望捕获命令运行时给出的任何stderr/stdout输出。随附带有免责声明的红色方框:

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

显然,我也想避免僵局


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

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

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

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

python示例2.py 我有实时输出(我想这是真的:):

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

“在ps中显示为“失效”意味着您可能在一个系统上,
选择
fcntl
将起作用,即,您可以轻松地读取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()

也许您可以从或中获得有趣的信息,或者可以生成单独的线程来读取进程的
stdout
stderr
,这样您就可以调用
wait()
,而不用担心缓冲区被填满。复杂的部分是如果您想停止阅读
stderr
。见@DanGetz谢谢,我想这会有用的。如果我得到有效的解决方案,我会向你汇报。
import subprocess

cmd = "python example1.py"

print subprocess.check_output(cmd, shell=True)
hello 0
hello 1
hello 2
hello 3
hello 4
hello 5
hello 6
#!/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()
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)
""")]