Python子进程杀死超时

发布于 2021-01-29 15:00:48

我正在python中使用subprocess模块​​运行一些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秒后被杀死,但是实际上该进程正在完成120秒脚本,然后引发了Timeout
Exception。现在的问题是如何通过超时杀死子进程?

关注者
0
被浏览
67
1 个回答
  • 面试哥
    面试哥 2021-01-29
    为面试而生,有面试问题,就找面试哥。

    该文档明确指出应终止该进程:

    文档中获取subprocess.run


    timeout参数传递给Popen.communicate()。如果超时到期,子进程将被杀死并等待。子进程终止后,将重新引发TimeoutExpired异常。”

    但是在您的情况下,您正在使用shell=True,而且我之前也遇到过类似的问题,因为阻塞过程是Shell过程的子过程。

    我认为,shell=True如果适当地分解参数并且脚本具有适当的shebang,则不需要。您可以尝试以下方法:

    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=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()
    

    (资料来源:如何从python中杀死进程和子进程?

    该示例也会杀死记事本实例。



知识点
面圈网VIP题库

面圈网VIP题库全新上线,海量真题题库资源。 90大类考试,超10万份考试真题开放下载啦

去下载看看