简体   繁体   English

Python子进程杀死超时

[英]Python subprocess kill with timeout

I am running some shell scripts with the subprocess module in python. 我正在python中使用subprocess模块​​运行一些shell脚本。 If the shell scripts is running to long, I like to kill the subprocess. 如果shell脚本运行时间很长,我想杀死该子进程。 I thought it will be enough if I am passing the timeout=30 to my run(..) statement. 我认为如果将timeout=30传递给run(..)语句就足够了。

Here is the code: 这是代码:

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:

I have tested this call with some shell scripts that runs 120s. 我已经用一些运行120秒的shell脚本测试了此调用。 I expected the subprocess to be killed after 30s, but in fact the process is finishing the 120s script and than raises the Timeout Exception. 我期望子进程在30秒后被杀死,但是实际上该进程正在完成120秒脚本,然后引发了Timeout Exception。 Now the Question how can I kill the subprocess by timeout? 现在的问题是如何通过超时杀死子进程?

The documentation explicitly states that the process should be killed: 该文档明确指出应终止该进程:

from the docs for subprocess.run : 文档的subprocess.run

"The timeout argument is passed to Popen.communicate(). If the timeout expires, the child process will be killed and waited for. The TimeoutExpired exception will be re-raised after the child process has terminated." “ timeout参数传递给Popen.communicate()。如果超时到期,子进程将被杀死并等待。子进程终止后,将重新引发TimeoutExpired异常。”

But in your case you're using shell=True , and I've seen issues like that before, because the blocking process is a child of the shell process. 但是在您的情况下,您使用的是shell=True ,而且我之前也遇到过类似的问题,因为阻塞过程是Shell过程的子过程。

I don't think you need shell=True if you decompose your arguments properly and your scripts have the proper shebang. 如果您正确分解参数并且脚本具有适当的shebang,我认为您不需要shell=True You could try this: 您可以尝试以下方法:

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)

note that I can reproduce this issue very easily on Windows (using Popen , but it's the same thing): 请注意,我可以在Windows上非常轻松地重现此问题(使用Popen ,但这是同一回事):

import subprocess,time

p=subprocess.Popen("notepad",shell=True)
time.sleep(1)
p.kill()

=> notepad stays open, probably because it manages to detach from the parent shell process. =>记事本保持打开状态,可能是因为它设法脱离了父shell进程。

import subprocess,time

p=subprocess.Popen("notepad",shell=False)
time.sleep(1)
p.kill()

=> notepad closes after 1 second =>记事本在1秒后关闭

Funnily enough, if you remove time.sleep() , kill() works even with shell=True probably because it successfully kills the shell which is launching notepad . 有趣的是,如果删除time.sleep()kill()即使在shell=True也可以工作,可能是因为它成功杀死了启动notepad的shell。

I'm not saying you have exactly the same issue, I'm just demonstrating that shell=True is evil for many reasons, and not being able to kill/timeout the process is one more reason. 我并不是说您有完全相同的问题,我只是出于种种原因证明shell=True是邪恶的,而无法终止进程/使其超时则是另一个原因。

However, if you need shell=True for a reason, you can use psutil to kill all the children in the end. 但是, 如果出于某种原因需要shell=True ,则可以使用psutil最终杀死所有子级。 In that case, it's better to use Popen so you get the process id directly: 在这种情况下,最好使用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()

(source: how to kill process and child processes from python? ) (资料来源: 如何从python中杀死进程和子进程?

that example kills the notepad instance as well. 该示例也会杀死记事本实例。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM