我正在 python 中使用 subprocess 模块运行一些 shell 脚本.如果 shell 脚本运行时间过长,我喜欢终止子进程.我认为如果我将 timeout=30
传递给我的 run(..)
语句就足够了.
I am running some shell scripts with the subprocess module in python. If the shell scripts is running to long, I like to kill the subprocess. I thought it will be enough if I am passing the timeout=30
to my run(..)
statement.
代码如下:
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 秒脚本,然后引发超时异常.现在的问题是如何通过超时杀死子进程?
I have tested this call with some shell scripts that runs 120s. 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. Now the Question how can I kill the subprocess by timeout?
文档明确指出应该终止进程:
The documentation explicitly states that the process should be killed:
来自 subprocess.run
的文档:
from the docs for subprocess.run
:
将超时参数传递给Popen.communicate().如果超时,子进程将被杀死并等待.子进程终止后将重新引发TimeoutExpired异常."
"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."
但在您的情况下,您使用的是 shell=True
,我之前也看到过类似的问题,因为阻塞进程是 shell 进程的子进程.
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.
如果您正确分解参数并且您的脚本具有正确的 shebang,我认为您不需要 shell=True
.你可以试试这个:
I don't think you need shell=True
if you decompose your arguments properly and your scripts have the proper shebang. 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)
请注意,我可以很容易地在 Windows 上重现此问题(使用 Popen
,但它是同一件事):
note that I can reproduce this issue very easily on Windows (using Popen
, but it's the same thing):
import subprocess,time
p=subprocess.Popen("notepad",shell=True)
time.sleep(1)
p.kill()
=> 记事本保持打开状态,可能是因为它设法与父 shell 进程分离.
=> notepad stays open, probably because it manages to detach from the parent shell process.
import subprocess,time
p=subprocess.Popen("notepad",shell=False)
time.sleep(1)
p.kill()
=> 记事本在 1 秒后关闭
=> notepad closes after 1 second
有趣的是,如果你删除 time.sleep()
,kill()
甚至可以使用 shell=True
可能是因为它成功杀死了正在启动 notepad
.
Funnily enough, if you remove time.sleep()
, kill()
works even with shell=True
probably because it successfully kills the shell which is launching notepad
.
我并不是说您有完全相同的问题,我只是证明 shell=True
出于多种原因是邪恶的,并且无法终止/超时该进程是另一个原因原因.
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
是有原因的,你可以使用psutil
最后杀死所有的孩子.在这种情况下,最好使用 Popen
直接获取进程 ID:
However, if you need shell=True
for a reason, you can use psutil
to kill all the children in the end. In that case, it's better to use Popen
so you get the process id directly:
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()
(来源:如何杀死进程和子进程蟒蛇?)
该示例也杀死了记事本实例.
that example kills the notepad instance as well.
这篇关于Python子进程超时终止的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持跟版网!