我在python中使用子进程模块运行一些shell脚本。如果shell脚本运行得很长,我喜欢杀死子进程。我认为如果我将timeout=30
传递给我的run(..)
声明就足够了。
以下是代码:
try:
result=run(['utilities/shell_scripts/{0} {1} {2}'.format(
self.language_conf[key][1], self.proc_dir, config.main_file)],
shell=True,
check=True,
stdout=PIPE,
stderr=PIPE,
universal_newlines=True,
timeout=30,
bufsize=100)
except TimeoutExpired as timeout:
我用一些运行120秒的shell脚本测试了这个调用。我预计子进程会在30秒后被杀死,但实际上这个过程正在完成120s脚本,而不是引发超时异常。现在问题如何通过超时杀死子进程?
答案 0 :(得分:4)
文档明确指出应该杀死该进程:
“超时参数传递给Popen.communicate()。如果超时到期,子进程将被终止并等待。在子进程终止后,将重新引发TimeoutExpired异常。”
但在你的情况下,你正在使用shell=True
,之前我已经看过类似的问题,因为阻塞过程是shell进程的子进程。
如果你正确地分解你的论点并且你的脚本有适当的shebang,我认为你不需要shell=True
。你可以试试这个:
result=run(
[os.path.join('utilities/shell_scripts',self.language_conf[key][1]), self.proc_dir, config.main_file], # don't compose argument line yourself
shell=False, # no shell wrapper
check=True,
stdout=PIPE,
stderr=PIPE,
universal_newlines=True,
timeout=30,
bufsize=100)
请注意,我可以在Windows上轻松地重现此问题(使用Popen
,但它是一样的):
import subprocess,time
p=subprocess.Popen("notepad",shell=True)
time.sleep(1)
p.kill()
=>记事本保持打开状态,可能是因为它设法从父shell进程中分离出来。
import subprocess,time
p=subprocess.Popen("notepad",shell=False)
time.sleep(1)
p.kill()
=>记事本在1秒后关闭
有趣的是,如果您删除time.sleep()
,kill()
可能会与shell=True
一起工作,因为它可能会成功杀死正在启动notepad
的shell。
我并不是说你有完全相同的问题,我只是因为很多原因证明shell=True
是邪恶的,而且无法杀死/超时这个过程是另一个原因。
但是,如果因某种原因需要shell=True
,您可以使用psutil
最终杀死所有孩子。在这种情况下,最好使用Popen
,以便直接获得进程ID:
import subprocess,time,psutil
parent=subprocess.Popen("notepad",shell=True)
for _ in range(30): # 30 seconds
if parent.poll() is not None: # process just ended
break
time.sleep(1)
else:
# the for loop ended without break: timeout
parent = psutil.Process(parent.pid)
for child in parent.children(recursive=True): # or parent.children() for recursive=False
child.kill()
parent.kill()
(来源:how to kill process and child processes from python?)
该示例也会杀死记事本实例。