Объект Python subprocess.Popen зависает, собирая дочерний вывод, когда дочерний процесс не завершается - PullRequest
2 голосов
/ 28 января 2010

Когда процесс завершается ненормально или не завершается вообще, я все еще хочу иметь возможность собирать, какой вывод он мог генерировать до этого момента.

Очевидное решение этого примера кода - уничтожить дочерний процесс с помощью 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)

В этом случае появляется сообщение print о тайм-ауте, и скрипт python никогда не завершается и не прогрессирует. Кто-нибудь знает, как я могу сделать это по-другому и все же получить вывод из моего дочернего процесса

Ответы [ 4 ]

1 голос
/ 11 февраля 2010

Вот способ POSIX сделать это без временного файла. Я понимаю, что подпроцесс здесь немного лишний, но поскольку в оригинальном вопросе он использовался ...

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)
1 голос
/ 28 января 2010

Проблема в том, что 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
0 голосов
/ 08 мая 2010

У меня была точно такая же проблема. В итоге я решил проблему (после поиска в Google и нахождения множества связанных проблем), просто установив следующие параметры при вызове subprocess.Popen (или .call):

stdout=None

и

stderr=None

Есть много проблем с этими функциями, но в моем конкретном случае я считаю, что stdout заполнялся процессом, который я вызывал, и затем приводил к условию блокировки. Установив для них значение None (в отличие от чего-то вроде subprocess.PIPE), я считаю, что этого избежать.

Надеюсь, это кому-нибудь поможет.

0 голосов
/ 28 января 2010

Есть еще несколько полезных советов в другом вопросе:

Большинство подсказок там работают с pipe.readline() вместо pipe.communicate(), потому что последний возвращается только в конце процесса.

...