首页 文章

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

提问于
浏览
4

现在我有一些代码使用来自子进程的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

    "shows as " defunct " in ps"意味着您可能位于 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

    我试过一个简单的例子 . 到目前为止我所拥有的只是捕获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
    

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

相关问题