问题:如何终止以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=Trueshell是子进程时,命令就是它的子进程。因此,任何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.
     
                 
             
            
         
        
        
	
	声明:本站所有文章,如无特殊说明或标注,均为本站原创发布。任何个人或组织,在未征得本站同意时,禁止复制、盗用、采集、发布本站内容到任何网站、书籍等各类媒体平台。如若本站内容侵犯了原著者的合法权益,可联系我们进行处理。