Shell = True ile başlatılan bir python alt işlemini sonlandırma


308

Aşağıdaki komutla bir alt işlem başlatıyorum:

p = subprocess.Popen(cmd, stdout=subprocess.PIPE, shell=True)

Ancak, kullanarak öldürmeye çalıştığınızda:

p.terminate()

veya

p.kill()

Komut arka planda çalışmaya devam ediyor, bu yüzden süreci nasıl sonlandırabileceğimi merak ediyordum.

Komutu ile çalıştırdığımda:

p = subprocess.Popen(cmd.split(), stdout=subprocess.PIPE)

p.terminate(). Verilirken başarıyla sonlandırılır .


Neye cmdbenziyor Başlatılacak birkaç işlemi tetikleyen bir komut içerebilir. Hangi süreç hakkında konuştuğunuz belli değil.
Robert Siemer


1
shell=Truebüyük bir fark yaratmıyor mu?
Charlie Parker

Yanıtlar:


410

Gruplardaki tüm sürece sinyal gönderilmesini sağlamak için bir süreç grubu kullanın . Bunun için , doğmuş / alt süreçlerin üst sürecine, sizin durumunuzda bir kabuk olan bir oturum kimliği eklemelisiniz . Bu onu süreçlerin grup lideri yapacaktır. Şimdi, süreç grubu liderine bir sinyal gönderildiğinde, bu grubun tüm alt süreçlerine iletilir.

İşte kod:

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

2
@PiotrDobrogost: Ne yazık ki hayır, çünkü os.setsidpencerelerde mevcut değil ( docs.python.org/library/os.html#os.setsid ), bunun yardımcı olup olmadığını bilmiyorum ama buraya bakabilirsiniz ( bugs.python.org / issue5115 ) nasıl yapılacağı hakkında bazı bilgiler için.
mouad

6
subprocess.CREATE_NEW_PROCESS_GROUPBununla nasıl bir ilişkisi var?
Piotr Dobrogost

11
testlerimiz setid! = setpgid olan ve os.pgkill'in yalnızca aynı işlem grubu kimliğine sahip alt işlemleri öldürdüğünü gösterir. aynı oturum kimliğine sahip olmalarına rağmen, işlem grubunu değiştiren işlemler öldürülmez ...
hwjp


4
Ben de başka etkileri olduğu için os.setsid () yapmanızı tavsiye etmem. Diğerlerinin yanı sıra kontrol eden TTY'nin bağlantısını keser ve yeni süreci bir süreç grubu lideri yapar. Bkz. Win.tue.nl/~aeb/linux/lk/lk-10.html
parasietje

92
p = subprocess.Popen(cmd, stdout=subprocess.PIPE, shell=True)
p.kill()

p.kill()kabuk işlemini öldürür ve cmdhala çalışır.

Bu uygun bir düzeltme buldum:

p = subprocess.Popen("exec " + cmd, stdout=subprocess.PIPE, shell=True)

Bu, cmd'nin, kabuğun öldürülmeyen bir alt işlemi başlatmasını sağlamak yerine kabuk işlemini miras almasına neden olur. p.pido zaman cmd sürecinizin kimliği olacaktır.

p.kill() çalışmalı.

Bunun borunuz üzerinde ne gibi bir etkisi olacağını bilmiyorum.


1
* Nix için güzel ve hafif bir çözüm, teşekkürler! Linux üzerinde çalışır, Mac için de çalışmalıdır.
MarSoft

Çok güzel bir çözüm. Eğer cmd'niz başka bir şey için bir kabuk komut dosyası sarıcısı olursa, sadece bir alt işleme sahip olmak için son ikili dosyayı exec ile çağırın.
Nicolinux

1
Bu güzel. Nasıl Ubuntu çalışma alanı başına bir alt işlem yumurtlamak ve öldürmek için anlamaya çalışıyorum. Bu cevap bana yardımcı oldu. Keşke ben bir kez daha fazla oy verebilir
Sergiy Kolodyazhnyy


@speedyrazor - Windows10'da çalışmaz. Bence os'a özgü cevaplar açıkça bu şekilde işaretlenmelidir.
DarkLight

48

Psutil'i kullanabiliyorsanız , bu mükemmel çalışır:

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
AttributeError: 'Process' object has no attribute 'get_childreniçin pip install psutil.
d33tah

3
Bence get_children () çocuklar () olmalı. Ama Windows'ta benim için çalışmadı, süreç hala orada.
Godsmith

3
@Godsmith - psutil API değişti ve haklısınız: children () , get_children () ile aynı şeyi yapıyor. Windows'da çalışmazsa, GitHub'da
Jovik

24

Kullanarak yapabilirdim

from subprocess import Popen

process = Popen(command, shell=True)
Popen("TASKKILL /F /PID {pid} /T".format(pid=process.pid))

cmd.exeve emrini verdiğim programı öldürdü .

(Windows'ta)


Veya işlem adını kullanın : Popen("TASKKILL /F /IM " + process_name)eğer yoksa, commandparametreden alabilirsiniz.
zvi

12

Ne zaman shell=Truekabuk çocuk süreçtir ve komutlar onun çocuklarıyız. Yani kabuğu öldürür SIGTERMveya SIGKILLöldürür, ama onun çocuk süreçlerini değil, ve bunu yapmanın iyi bir yolunu hatırlamıyorum. Düşünebileceğim en iyi yol kullanmaktır shell=False, aksi takdirde üst kabuk işlemini öldürdüğünüzde geçersiz bir kabuk işlemi bırakılır .


8

Bu cevapların hiçbiri benim için çalıştı bu yüzden işe yaradı kod bırakarak. Benim durumumda bile süreci öldürdükten .kill()ve bir .poll()dönüş kodu aldıktan sonra süreç sona ermedi.

subprocess.Popen Belgeleri takip ederek :

"... düzgün bir şekilde temizlemek için iyi davranılmış bir uygulama çocuk sürecini öldürmeli ve iletişimi bitirmelidir ..."

proc = subprocess.Popen(...)
try:
    outs, errs = proc.communicate(timeout=15)
except TimeoutExpired:
    proc.kill()
    outs, errs = proc.communicate()

Benim durumumda proc.communicate()sonra arama eksikti proc.kill(). Bu işlem stdin, stdout'u temizler ve süreci sonlandırır.


Bu çözüm Linux ve Python 2.7'de benim için çalışmıyor
user9869932

@xyz Linux ve python 3.5'te benim için çalıştı. Python 2.7
epinal

@ espinal, teşekkürler, evet. Muhtemelen bir linux sorunu. Bir Raspberry 3 üzerinde çalışan Raspbian linux
user9869932

5

Sai'nin dediği gibi, kabuk çocuktur, bu yüzden sinyaller onun tarafından kesilir - bulduğum en iyi yol shell = False kullanmak ve komut satırını bölmek için shlex kullanmaktır:

if isinstance(command, unicode):
    cmd = command.encode('utf8')
args = shlex.split(cmd)

p = subprocess.Popen(args, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)

Sonra p.kill () ve p.terminate () beklediğiniz gibi çalışmalıdır.


1
Benim durumumda cmd "cd yolu && zsync vb vs" göz önüne alındığında gerçekten yardımcı olmaz. Bu aslında komutun başarısız olmasını sağlar!
user175259

4
Dizinleri değiştirmek yerine mutlak yollar kullanın ... İsteğe bağlı olarak o dizine os.chdir (...) ...
Matt Billenstein

Alt işlem için çalışma dizinini değiştirme yeteneği yerleşiktir. Sadece cwdargümanı şuraya ilet Popen.
Jonathon Reinhart

Shlex kullandım, ama yine de sorun devam ediyor, öldürmek çocuk süreçlerini öldürmüyor.
hungryWolf

1

ne kullanabilirsiniz gibi hissediyorum:

import os
import signal
import subprocess
p = subprocess.Popen(cmd, stdout=subprocess.PIPE, shell=True)

os.killpg(os.getpgid(pro.pid), signal.SIGINT)

bu, tüm görevinizi değil, p.pid ile işlemi öldürecektir.


0

Bunun eski bir soru olduğunu biliyorum ama bu farklı bir yöntem arayan birine yardımcı olabilir. Aradığım işlemleri öldürmek için pencerelerde kullandığım bu.

si = subprocess.STARTUPINFO()
si.dwFlags |= subprocess.STARTF_USESHOWWINDOW
subprocess.call(["taskkill", "/IM", "robocopy.exe", "/T", "/F"], startupinfo=si)

/ IM resim adıdır, isterseniz / PID de yapabilirsiniz. / T süreci ve alt süreçleri öldürür. / F kuvveti onu sonlandırır. si, ayarladığım gibi, bunu bir CMD penceresi göstermeden nasıl yaptığınızdır. Bu kod python 3'te kullanılır.


0

Sinyali gruptaki tüm süreçlere gönderme

    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)

0

Burada bahsettiğimi görmedim, bu yüzden birinin ihtiyacı olması durumunda onu dışarıya koyuyorum. Tek yapmanız gereken, alt işleminizin başarıyla sona erdiğinden emin olmaksa, onu bir içerik yöneticisine koyabilirsiniz. Örneğin, standart yazıcımın bir görüntü yazdırmasını ve bağlam yöneticisini kullanarak alt işlemin sonlandırılmasını sağladım.

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...')

Bu yöntemin de platformlar arası olduğuna inanıyorum.


Buradaki kodun bir işlemi nasıl sonlandırdığını göremiyorum. Açıklayabilir misin?
DarkLight

Kodunuzun bir sonraki satırına geçmeden önce tüm yapmanız gereken işleminizin sona erdiğinden emin olursanız, bu yöntemi kullanabileceğinizi açıkça belirtmiştim. Süreci sonlandırdığını iddia etmedim.
Jaiyam Sharma

Bağlam yöneticisi açıkça belirttiğimiz gibi, "senin işlemi sonlandırıldı emin olur" o zaman bunu o süreci sonlandırır iddiasını. Gerçi? Süreç ile başlatıldığında bile shell=True, soruya göre? Hangi kodunda değil .
anon

anon, 'feshedildi' kelimesinin kafa karıştırıcı kullanımına işaret ettiğiniz için teşekkür ederiz. Bağlam yöneticisi, son satırdaki print deyimine geçmeden önce alt sürecin tamamlanmasını bekler. Bu, bir sigterm gibi bir şey kullanarak süreci anında sonlandırmaktan farklıdır. Bir iş parçacığı kullanarak ve arayarak aynı sonucu elde edebilirsiniz thread.join(). Umarım bu onu temizler.
Jaiyam Sharma
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.