我有以下简单的python脚本:
import os, subprocess,signal,sys
import time
out = None
sub = None
def handler(signum,frame):
print("script.py: cached sig: %i " % signum)
sys.stdout.flush()
if sub is not None and not sub.poll():
print("render.py: sent signal to prman pid: ", sub.pid)
sys.stdout.flush()
sub.send_signal(signal.SIGTERM)
sub.wait() # deadlocks....????
#os.kill(sub.pid, signal.SIGTERM) # this works
#os.waitpid(sub.pid,0) # this works
for i in range(0,5):
time.sleep(0.1)
print("script.py: cleanup %i" % i)
sys.stdout.flush()
sys.exit(128+signum)
signal.signal(signal.SIGINT, handler)
signal.signal(signal.SIGUSR2, handler)
signal.signal(signal.SIGTERM, handler)
sub = subprocess.Popen(["./doStuff.sh"], stderr = subprocess.STDOUT)
sub.wait()
print("finished script.py")
doStuff.sh
#!/bin/bash
function trap_with_arg() {
func="$1" ; shift
for sig ; do
trap "$func $sig" "$sig"
done
}
pid=False
function signalHandler() {
trap - SIGINT SIGTERM
echo "doStuff.sh chached sig: $1"
echo "doStuff.sh cleanup: wait 10s"
sleep 10s
# kill ourself to signal calling process we exited on SIGINT
kill -s SIGINT $$
}
trap_with_arg signalHandler SIGINT SIGTERM
trap "echo 'doStuff.sh ignore SIGUSR2'" SIGUSR2
# ignore SIGUSR2
echo "doStuff.sh : pid: $$"
echo "doStuff.sh: some stub error" 1>&2
for i in {1..100}; do
sleep 1s
echo "doStuff.sh, rendering $i"
done
当我发送在终端上发起的进程时
python3 scripts.py &
kill -USR2 -$!
的信号
该脚本捕获SIGINT,并在sub.wait()
中永远等待,ps -uf
显示以下内容:。
user 27515 0.0 0.0 29892 8952 pts/22 S 21:56 0:00 \_ python script.py
user 27520 0.0 0.0 0 0 pts/22 Z 21:56 0:00 \_ [doStuff.sh] <defunct>
请注意doStuff.sh
正确处理SIGINT并退出。
我还希望在调用handler
时得到stdout的输出?如何正确地做到这一点?
非常感谢!
答案 0 :(得分:1)
您的代码无法获得子流程&#39; stdout因为它在调用subprocess.Popen()
时没有重定向其标准流。在信号处理程序中做任何事都为时已晚。
如果您想捕获标准输出,请传递stdout=subprocess.PIPE
并致电.communicate()
而不是.wait()
:
child = subprocess.Popen(command, stdout=subprocess.PIPE)
output = child.communicate()[0]
有一个完全独立的问题,信号处理程序在Python 3上的.wait()
调用中挂起(Python 2或os.waitpid()
不会挂起,但会收到错误的子项退出状态代替)。这是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
其中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')
示例:
$ python3 parent.py &
$ kill -USR1 $!
child exits on KeyboardInterrupt
$ fg
... running python3 parent.py
该示例显示孩子已退出但父母仍在运行。如果按 Ctrl + C 中断它;回溯显示它挂在with _self._waitpid_lock:
调用内的.wait()
语句中。如果self._waitpid_lock = threading.Lock()
替换为self._waitpid_lock = threading.RLock()
中的subprocess.py
,则效果与使用os.waitpid()
相同 - 它不会挂起,但退出状态不正确。< / p>
为了避免这个问题,不要在信号处理程序中等待子状态:调用send_signal()
,设置一个简单的布尔标志,然后从hanlder返回。在主代码中,检查child.wait()
之后的标志(问题代码中的print("finished script.py")
之前),看看是否收到了信号(如果child.returncode
不清楚) 。如果设置了标志;调用适当的清理代码并退出。
答案 1 :(得分:0)
您应该查看 subprocess.check_output
proc_output = subprocess.check_output(commands_list, stderr=subprocess.STDOUT)
你可以在尝试除了然后再包围它:
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
)
答案 2 :(得分:0)
我只能使用
等待这个过程 os.kill(sub.pid, signal.SIGINT)
os.waitpid(sub.pid,0)
而不是
sub.send_signal(signal.SIGINT)
sub.wait() # blocks forever
这与UNIX上的进程组有关,我真的不明白:我认为进程./doStuff.sh
没有收到信号,因为同一进程组中的子进程没有收到信号。 (我不确定这是否正确)。希望有人可能会再详细说明这个问题。
调用处理程序之前的输出被推送到调用bash(控制台)的stdout。