how to get stdout of subprocess in python when receving SIGUSR2 /SIGINT

你。 提交于 2019-12-06 00:39:48

Your code can't get the child process' stdout because it doesn't redirect its standard streams while calling subprocess.Popen(). It is too late to do anything about it in the signal handler.

If you want to capture stdout then pass stdout=subprocess.PIPE and call .communicate() instead of .wait():

child = subprocess.Popen(command, stdout=subprocess.PIPE)
output = child.communicate()[0]

There is a completely separate issue that the signal handler hangs on the .wait() call on Python 3 (Python 2 or os.waitpid() do not hang here but a wrong child's exit status is received instead). Here's a minimal code example to reproduce the issue:

#!/usr/bin/env python
import signal
import subprocess
import sys


def sighandler(*args):
    child.send_signal(signal.SIGINT)
    child.wait()  # It hangs on Python 3 due to child._waitpid_lock

signal.signal(signal.SIGUSR1, sighandler)
child = subprocess.Popen([sys.executable, 'child.py'])
sys.exit("From parent %d" % child.wait())  # return child's exit status

where child.py:

#!/usr/bin/env python
"""Called from parent.py"""
import sys
import time

try:
    while True:
        time.sleep(1)
except KeyboardInterrupt:  # handle SIGINT
    sys.exit('child exits on KeyboardInterrupt')

Example:

$ python3 parent.py &
$ kill -USR1 $!
child exits on KeyboardInterrupt
$ fg
... running    python3 parent.py

The example shows that the child has exited but the parent is still running. If you press Ctrl+C to interrupt it; the traceback shows that it hangs on with _self._waitpid_lock: statement inside the .wait() call. If self._waitpid_lock = threading.Lock() is replaced with self._waitpid_lock = threading.RLock() in subprocess.py then the effect is the same as using os.waitpid() -- it doesn't hang but the exit status is incorrect.

To avoid the issue, do not wait for child's status in the signal handler: call send_signal(), set a simple boolean flag and return from the hanlder instead. In the main code, check the flag after child.wait() (before print("finished script.py") in your code in the question), to see whether the signal has been received (if it is not clear from child.returncode). If the flag is set; call the appropriate cleanup code and exit.

You should look into subprocess.check_output

proc_output = subprocess.check_output(commands_list, stderr=subprocess.STDOUT)

you can surround it in a try except and then:

except subprocess.CalledProcessError, error:
    create_log = u"Creation Failed with return code {return_code}\n{proc_output}".format(
        return_code=error.returncode, proc_output=error.output
    )

I can only wait for the process by using

  os.kill(sub.pid, signal.SIGINT)
  os.waitpid(sub.pid,0)

instead of

  sub.send_signal(signal.SIGINT)
  sub.wait() # blocks forever

This has something to do with process groups on UNIX, which I dont really understand: I think the process ./doStuff.sh does not receive the signal because childs in the same process groups do not receive the signal. (I am not sure if this is correct). Hopefully somebody might elaborate on this issue a bit more.

The output till the handler gets called is pushed to the stdout of the calling bash (console).

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!