【问题标题】:Python's subprocess.Popen object hangs gathering child output when child process does not exit当子进程不退出时,Python subprocess.Popen 对象挂起收集子输出
【发布时间】:2010-01-28 01:14:49
【问题描述】:

当一个进程异常退出或根本不退出时,我仍然希望能够收集到那时它可能生成的输出。

此示例代码的明显解决方案是使用 os.kill 杀死子进程,但在我的实际代码中,子进程挂起等待 NFS 并且不响应 SIGKILL。

#!/usr/bin/python
import subprocess
import os
import time
import signal
import sys
child_script = """
#!/bin/bash
i=0
while [ 1 ]; do
    echo "output line $i"
    i=$(expr $i \+ 1)
    sleep 1
done
"""
childFile = open("/tmp/childProc.sh", 'w')
childFile.write(child_script)
childFile.close()

cmd = ["bash", "/tmp/childProc.sh"]
finish = time.time() + 3
p = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, stdin=subprocess.PIPE)
while p.poll() is None:
    time.sleep(0.05)
    if finish < time.time():
        print "timed out and killed child, collecting what output exists so far"
        out, err = p.communicate()
        print "got it"
        sys.exit(0)

在这种情况下,会出现有关超时的打印语句,并且 python 脚本永远不会退出或继续。有谁知道我可以如何以不同的方式做到这一点,并且仍然从我的子进程中获得输出

【问题讨论】:

  • 如果你删除 p.communicate(),它就会退出。但你到底想做什么?

标签: python subprocess freeze


【解决方案1】:

问题是 bash 在未连接终端时不响应 CTRL-C。 切换到 SIGHUP 或 SIGTERM 似乎可以解决问题:

cmd = ["bash", 'childProc.sh']
p = subprocess.Popen(cmd, stdout=subprocess.PIPE, 
                          stderr=subprocess.STDOUT, 
                          close_fds=True)
time.sleep(3)
print 'killing pid', p.pid
os.kill(p.pid, signal.SIGTERM)
print "timed out and killed child, collecting what output exists so far"
out  = p.communicate()[0]
print "got it", out

输出:

killing pid 5844
timed out and killed child, collecting what output exists so far
got it output line 0
output line 1
output line 2

【讨论】:

    【解决方案2】:

    这是一种没有临时文件的 POSIX 方式。我意识到 subprocess 在这里有点多余,但是由于原始问题使用了它...

    import subprocess
    import os
    import time
    import signal
    import sys
    
    pr, pw = os.pipe()
    pid = os.fork () 
    
    if pid: #parent
        os.close(pw)
        cmd = ["bash"]
        finish = time.time() + 3
        p = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, stdin=pr, close_fds=True)
        while p.poll() is None:
            time.sleep(0.05)
            if finish < time.time():
                os.kill(p.pid, signal.SIGTERM)
                print "timed out and killed child, collecting what output exists so far"
                out, err = p.communicate()
                print "got it: ", out
                sys.exit(0)
    
    else: #child
        os.close(pr)
        child_script = """
        #!/bin/bash
        while [ 1 ]; do
            ((++i))
            echo "output line $i"
            sleep 1
        done
        """
        os.write(pw, child_script)
    

    【讨论】:

      【解决方案3】:

      另一个stackoverflow问题中有很好的提示:How do I get 'real-time' information back from a subprocess.Popen in python (2.5)

      其中的大多数提示都使用pipe.readline() 而不是pipe.communicate(),因为后者仅在进程结束时返回。

      【讨论】:

        【解决方案4】:

        我遇到了完全相同的问题。我最终通过在调用subprocess.Popen(或.call)时简单地设置以下参数来解决问题(在搜索谷歌并找到许多相关问题之后):

        stdout=None
        

        stderr=None
        

        这些函数存在很多问题,但在我的具体情况下,我相信 stdout 被我调用的进程填满,然后导致阻塞情况。通过将这些设置为None(与subprocess.PIPE 相对),我相信可以避免这种情况。

        希望这对某人有所帮助。

        【讨论】:

          猜你喜欢
          • 2018-03-11
          • 1970-01-01
          • 2010-09-21
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2019-08-01
          • 1970-01-01
          相关资源
          最近更新 更多