带超时的Python子进程终止
我正在使用python中的子流程模块运行一些shell脚本。如果shell脚本运行时间过长,我希望终止子进程。我想如果我将带超时的Python子进程终止,python,subprocess,Python,Subprocess,我正在使用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)],
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:
我已经用一些运行120s的shell脚本测试了这个调用。我预计子进程将在30秒后终止,但实际上,该进程正在完成120秒脚本,然后引发超时异常。现在的问题是,如何通过超时终止子进程?文档明确指出应终止该进程: 从: 超时参数被传递给Popen.communicate()。如果超时过期,子进程将被终止并等待。子进程终止后,将重新引发TimeoutExpired异常 但是在您的例子中,您使用的是
shell=True
,我以前见过类似的问题,因为阻塞过程是shell过程的子过程
import subprocess,time
p=subprocess.Popen("notepad",shell=False)
time.sleep(1)
p.kill()
如果你正确地分解你的参数并且你的脚本有正确的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
,但这是一样的):
=>记事本保持打开状态,可能是因为它设法与父shell进程分离
import subprocess,time
p=subprocess.Popen("notepad",shell=False)
time.sleep(1)
p.kill()
=>记事本在1秒后关闭
有趣的是,如果删除time.sleep()
,kill()
即使与shell=True
一起工作,可能是因为它成功地杀死了正在启动记事本的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()
(来源:)
该示例也会杀死记事本实例。您是否使用Popen
尝试过遗留方法?除了块,您的中还有什么?在文档中:“如果超时过期,子进程不会被终止,因此为了正确清理,行为良好的应用程序应该终止子进程并完成通信。”我已经阅读了官方文档,它发送SIGKILL
来终止子进程。也许您的脚本不能被SIGKILL
杀死?在原始终端中试用。@cdarke这是Popen
的行为,而不是run
run
将终止子进程。@Sraw:对不起,你说得对。我仍然想知道块中除了
之外还有什么。在运行
的源代码中,它还使用kill()
终止子进程。所以在这种情况下,我认为这是行不通的。但是谁知道呢…@Sraw:我想是血壳=真的引起了这个问题。我。。。我还不确定,因为我从未见过这种行为。既然你说你已经看到了,你能帮我们一个忙,提供一个会导致这种情况的例子吗?@Sraw实际上我能。我使用的是windows,但我相信它也可以在Linux上复制。由于某些原因,我需要在shell中执行命令,因此有必要执行shell=true
。当我不使用该语句时,我得到一个FileNotFoundError
。或者该错误是由其他错误配置引起的?