как завершить подпроцесс python, запущенный с shell = true

<p>я запускаю подпроцесс с помощью следующей команды:</p><pre><code>p = subprocess.popen(cmd, stdout=subprocess.pipe, shell=true) </code></pre><p>тем не менее, когда я пытаюсь убить с помощью:</p><pre><code>p.terminate() </code></pre><p>или же </p><pre><code>p.kill() </code></pre><p>команда продолжает работать в фоновом режиме, поэтому мне было интересно, как я могу на самом деле завершить процесс. </p><p>обратите внимание, что когда я запускаю команду с:</p><pre><code>p = subprocess.popen(cmd.split(), stdout=subprocess.pipe) </code></pre><p>он успешно завершается при выдаче <code>p.terminate()</code>.</p>

<p>используйте <a href="http://en.wikipedia.org/wiki/process\_group">группу процессов</a> , чтобы разрешить отправку сигнала всем процессам в группах. для этого вы должны прикрепить <a href="http://pubs.opengroup.org/onlinepubs/009695399/functions/setsid.html">идентификатор сессии</a> к родительскому процессу порожденных/дочерних процессов, который в вашем случае является shell. это сделает его лидером группы процессов. поэтому теперь, когда сигнал отправляется руководителю группы процессов, он передается всем дочерним процессам этой группы.</p><p>вот код:</p><pre><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 </code></pre>

<pre><code>p = subprocess.popen(cmd, stdout=subprocess.pipe, shell=true) p.kill() </code></pre><p><code>p.kill()</code> завершает работу процесса shell, а <code>cmd</code> все еще выполняется.</p><p>я нашел удобное решение этой проблемы:</p><pre><code>p = subprocess.popen("exec " + cmd, stdout=subprocess.pipe, shell=true) </code></pre><p>это заставит cmd наследовать процесс shell вместо того, чтобы shell запускала дочерний процесс, который не был уничтожен. тогда <code>p.pid</code> будет идентификатором вашего процесса cmd.</p><p><code>p.kill()</code> должен работать.</p><p>я не знаю, как это повлияет на твою трубу.</p>

<p>если вы можете использовать <a href="https://pypi.python.org/pypi/psutil/">psutil</a> , то это прекрасно работает:</p><pre><code>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) </code></pre>

<p>я мог бы сделать это с помощью </p><pre><code>from subprocess import popen process = popen(command, shell=true) popen("taskkill /f /pid {pid} /t".format(pid=process.pid)) </code></pre><p>это убило <code>cmd.exe</code> и программу, для которой я дал команду.</p><p>(в windows)</p>

<p>когда <code>shell=true</code>, shell является дочерним процессом, а команды - его дочерними. таким образом, любой <code>sigterm</code> или <code>sigkill</code> убьет shell, но не его дочерние процессы, и я не помню хорошего способа сделать это . лучший способ, о котором я могу подумать, - это использовать <code>shell=false</code>, иначе, когда вы убьете родительский процесс shell, он оставит несуществующий процесс shell.</p>

<p>ни один из этих ответов не работал для меня, поэтому я оставляю код, который работал. в моем случае даже после завершения процесса с помощью <code>.kill()</code> и получения кода возврата <code>.poll()</code> процесс не завершился. </p><p>следуя <code>subprocess.popen</code> <a href="https://docs.python.org/3/library/subprocess.html">документации</a> :</p><blockquote> <p>«... для правильной очистки приложение с хорошим поведением должно завершить дочерний процесс и завершить связь ...»</p> </blockquote><pre><code>proc = subprocess.popen(...) try: outs, errs = proc.communicate(timeout=15) except timeoutexpired: proc.kill() outs, errs = proc.communicate() </code></pre><p>в моем случае я пропустил <code>proc.communicate()</code> после вызова <code>proc.kill()</code>. это очищает процесс stdin, stdout ... и завершает процесс.</p>

<p>как сказал сай, shell является дочерней, поэтому она перехватывает сигналы - лучший способ, который я нашел, это использовать shell = false и использовать shlex для разделения командной строки:</p><pre><code>if isinstance(command, unicode): cmd = command.encode('utf8') args = shlex.split(cmd) p = subprocess.popen(args, stdout=subprocess.pipe, stderr=subprocess.stdout) </code></pre><p>тогда p.kill () и p.terminate () должны работать так, как вы ожидаете.</p>

<p>что я чувствую, что мы могли бы использовать:</p><pre><code>import os import signal import subprocess p = subprocess.popen(cmd, stdout=subprocess.pipe, shell=true) os.killpg(os.getpgid(pro.pid), signal.sigint) </code></pre><p>это не убьет всю вашу задачу, но процесс с p.pid</p>

<p>я знаю, что это старый вопрос, но это может помочь кому-то искать другой метод. это то, что я использую в windows, чтобы убить процессы, которые я вызвал. </p><pre><code>si = subprocess.startupinfo() si.dwflags |= subprocess.startf\_useshowwindow subprocess.call(["taskkill", "/im", "robocopy.exe", "/t", "/f"], startupinfo=si) </code></pre><p>/ im - это имя изображения, вы также можете сделать/pid, если хотите./t убивает процесс, а также дочерние процессы./f сила прекращает его. си, как я установил, это то, как вы делаете это, не показывая окно cmd. этот код используется в python 3.</p>

<p>отправить сигнал всем процессам в группе</p><pre><code> 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) </code></pre>