问题:如何终止以shell = True启动的python子进程
我正在使用以下命令启动子流程:
p = subprocess.Popen(cmd, stdout=subprocess.PIPE, shell=True)
但是,当我尝试杀死使用:
p.terminate()
要么
p.kill()
该命令一直在后台运行,所以我想知道如何才能真正终止该过程。
请注意,当我使用以下命令运行命令时:
p = subprocess.Popen(cmd.split(), stdout=subprocess.PIPE)
发出时,它确实成功终止p.terminate()
。
I’m launching a subprocess with the following command:
p = subprocess.Popen(cmd, stdout=subprocess.PIPE, shell=True)
However, when I try to kill using:
p.terminate()
or
p.kill()
The command keeps running in the background, so I was wondering how can I actually terminate the process.
Note that when I run the command with:
p = subprocess.Popen(cmd.split(), stdout=subprocess.PIPE)
It does terminate successfully when issuing the p.terminate()
.
回答 0
使用进程组,以便能够向组中的所有进程发送信号。为此,您应该将会话ID附加到生成的子进程的父进程中,在您的情况下,这是一个外壳程序。这将使其成为流程的小组负责人。因此,现在,当信号发送到流程组负责人时,它便被传输到该组的所有子流程。
这是代码:
import os
import signal
import subprocess
# The os.setsid() is passed in the argument preexec_fn so
# it's run after the fork() and before exec() to run the shell.
pro = subprocess.Popen(cmd, stdout=subprocess.PIPE,
shell=True, preexec_fn=os.setsid)
os.killpg(os.getpgid(pro.pid), signal.SIGTERM) # Send the signal to all the process groups
Use a process group so as to enable sending a signal to all the process in the groups. For that, you should attach a session id to the parent process of the spawned/child processes, which is a shell in your case. This will make it the group leader of the processes. So now, when a signal is sent to the process group leader, it’s transmitted to all of the child processes of this group.
Here’s the code:
import os
import signal
import subprocess
# The os.setsid() is passed in the argument preexec_fn so
# it's run after the fork() and before exec() to run the shell.
pro = subprocess.Popen(cmd, stdout=subprocess.PIPE,
shell=True, preexec_fn=os.setsid)
os.killpg(os.getpgid(pro.pid), signal.SIGTERM) # Send the signal to all the process groups
回答 1
p = subprocess.Popen(cmd, stdout=subprocess.PIPE, shell=True)
p.kill()
p.kill()
最终终止了shell进程,cmd
并且仍在运行。
我通过以下方法找到了一个方便的解决方法:
p = subprocess.Popen("exec " + cmd, stdout=subprocess.PIPE, shell=True)
这将导致cmd继承shell进程,而不是让shell启动不会被杀死的子进程。 p.pid
然后将是您的cmd进程的ID。
p.kill()
应该管用。
我不知道这会对您的管道产生什么影响。
p = subprocess.Popen(cmd, stdout=subprocess.PIPE, shell=True)
p.kill()
p.kill()
ends up killing the shell process and cmd
is still running.
I found a convenient fix this by:
p = subprocess.Popen("exec " + cmd, stdout=subprocess.PIPE, shell=True)
This will cause cmd to inherit the shell process, instead of having the shell launch a child process, which does not get killed. p.pid
will be the id of your cmd process then.
p.kill()
should work.
I don’t know what effect this will have on your pipe though.
回答 2
如果可以使用psutil,则可以完美地工作:
import subprocess
import psutil
def kill(proc_pid):
process = psutil.Process(proc_pid)
for proc in process.children(recursive=True):
proc.kill()
process.kill()
proc = subprocess.Popen(["infinite_app", "param"], shell=True)
try:
proc.wait(timeout=3)
except subprocess.TimeoutExpired:
kill(proc.pid)
If you can use psutil, then this works perfectly:
import subprocess
import psutil
def kill(proc_pid):
process = psutil.Process(proc_pid)
for proc in process.children(recursive=True):
proc.kill()
process.kill()
proc = subprocess.Popen(["infinite_app", "param"], shell=True)
try:
proc.wait(timeout=3)
except subprocess.TimeoutExpired:
kill(proc.pid)
回答 3
我可以用
from subprocess import Popen
process = Popen(command, shell=True)
Popen("TASKKILL /F /PID {pid} /T".format(pid=process.pid))
它杀死了cmd.exe
我给命令的程序。
(在Windows上)
I could do it using
from subprocess import Popen
process = Popen(command, shell=True)
Popen("TASKKILL /F /PID {pid} /T".format(pid=process.pid))
it killed the cmd.exe
and the program that i gave the command for.
(On Windows)
回答 4
当shell=True
shell是子进程时,命令就是它的子进程。因此,任何SIGTERM
或SIGKILL
将杀死外壳程序但不会杀死它的子进程的过程,我不记得有什么好方法。我能想到的最好方法是使用shell=False
,否则当您杀死父shell进程时,它将留下一个已失效的shell进程。
When shell=True
the shell is the child process, and the commands are its children. So any SIGTERM
or SIGKILL
will kill the shell but not its child processes, and I don’t remember a good way to do it.
The best way I can think of is to use shell=False
, otherwise when you kill the parent shell process, it will leave a defunct shell process.
回答 5
这些答案均不适合我,因此我留下了有效的代码。就我而言,即使在终止进程.kill()
并获得.poll()
返回代码后,进程也没有终止。
按照subprocess.Popen
文档:
“ …为了正确清理行为良好的应用程序,应终止子进程并完成通信…”
proc = subprocess.Popen(...)
try:
outs, errs = proc.communicate(timeout=15)
except TimeoutExpired:
proc.kill()
outs, errs = proc.communicate()
就我而言,我proc.communicate()
在打电话后就错过了proc.kill()
。这将清理进程stdin,stdout …,并终止进程。
None of this answers worked for me so Im leaving the code that did work. In my case even after killing the process with .kill()
and getting a .poll()
return code the process didn’t terminate.
Following the subprocess.Popen
documentation:
“…in order to cleanup properly a well-behaved application should kill the child process and finish communication…”
proc = subprocess.Popen(...)
try:
outs, errs = proc.communicate(timeout=15)
except TimeoutExpired:
proc.kill()
outs, errs = proc.communicate()
In my case I was missing the proc.communicate()
after calling proc.kill()
. This cleans the process stdin, stdout … and does terminate the process.
回答 6
正如Sai所说,shell是孩子,因此信号被它拦截了-我发现的最佳方法是使用shell = False并使用shlex拆分命令行:
if isinstance(command, unicode):
cmd = command.encode('utf8')
args = shlex.split(cmd)
p = subprocess.Popen(args, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
然后p.kill()和p.terminate()应该可以按照您的期望工作。
As Sai said, the shell is the child, so signals are intercepted by it — best way I’ve found is to use shell=False and use shlex to split the command line:
if isinstance(command, unicode):
cmd = command.encode('utf8')
args = shlex.split(cmd)
p = subprocess.Popen(args, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
Then p.kill() and p.terminate() should work how you expect.
回答 7
我觉得我们可以使用:
import os
import signal
import subprocess
p = subprocess.Popen(cmd, stdout=subprocess.PIPE, shell=True)
os.killpg(os.getpgid(pro.pid), signal.SIGINT)
这不会杀死您的所有任务,但会杀死p.pid进程
what i feel like we could use:
import os
import signal
import subprocess
p = subprocess.Popen(cmd, stdout=subprocess.PIPE, shell=True)
os.killpg(os.getpgid(pro.pid), signal.SIGINT)
this will not kill all your task but the process with the p.pid
回答 8
我知道这是一个古老的问题,但这可能会对寻求其他方法的人有所帮助。这就是我在Windows上用来杀死已调用进程的方法。
si = subprocess.STARTUPINFO()
si.dwFlags |= subprocess.STARTF_USESHOWWINDOW
subprocess.call(["taskkill", "/IM", "robocopy.exe", "/T", "/F"], startupinfo=si)
/ IM是图像名称,如果需要,您也可以执行/ PID。/ T杀死进程以及子进程。/ F强制终止它。如我所设置的,si是如何执行此操作而不显示CMD窗口。此代码在python 3中使用。
I know this is an old question but this may help someone looking for a different method. This is what I use on windows to kill processes that I’ve called.
si = subprocess.STARTUPINFO()
si.dwFlags |= subprocess.STARTF_USESHOWWINDOW
subprocess.call(["taskkill", "/IM", "robocopy.exe", "/T", "/F"], startupinfo=si)
/IM is the image name, you can also do /PID if you want. /T kills the process as well as the child processes. /F force terminates it. si, as I have it set, is how you do this without showing a CMD window. This code is used in python 3.
回答 9
将信号发送到组中的所有进程
self.proc = Popen(commands,
stdout=PIPE,
stderr=STDOUT,
universal_newlines=True,
preexec_fn=os.setsid)
os.killpg(os.getpgid(self.proc.pid), signal.SIGHUP)
os.killpg(os.getpgid(self.proc.pid), signal.SIGTERM)
Send the signal to all the processes in group
self.proc = Popen(commands,
stdout=PIPE,
stderr=STDOUT,
universal_newlines=True,
preexec_fn=os.setsid)
os.killpg(os.getpgid(self.proc.pid), signal.SIGHUP)
os.killpg(os.getpgid(self.proc.pid), signal.SIGTERM)
回答 10
我在这里没有看到此内容,因此我将其放在此处,以防有人需要。如果您要做的只是确保子流程成功终止,则可以将其放在上下文管理器中。例如,我希望我的标准打印机打印出图像,并使用上下文管理器确保子进程终止。
import subprocess
with open(filename,'rb') as f:
img=f.read()
with subprocess.Popen("/usr/bin/lpr", stdin=subprocess.PIPE) as lpr:
lpr.stdin.write(img)
print('Printed image...')
我相信这种方法也是跨平台的。
I have not seen this mentioned here, so I am putting it out there in case someone needs it. If all you want to do is to make sure that your subprocess terminates successfully, you could put it in a context manager. For example, I wanted my standard printer to print an out image and using the context manager ensured that the subprocess terminated.
import subprocess
with open(filename,'rb') as f:
img=f.read()
with subprocess.Popen("/usr/bin/lpr", stdin=subprocess.PIPE) as lpr:
lpr.stdin.write(img)
print('Printed image...')
I believe this method is also cross-platform.