Yönetici Özeti (veya "tl; dr" sürümü): en fazla bir tane olduğunda kolay subprocess.PIPE, aksi takdirde zor.
İşini nasıl yaptığını biraz açıklamanın zamanı geldi subprocess.Popen.
(Dikkat: Bu Python 2.x içindir, ancak 3.x benzerdir; ve Windows varyantında oldukça bulanıkım. POSIX şeylerini çok daha iyi anlıyorum.)
PopenFonksiyon biraz aynı zamanda sıfır-üç I / O akışları ile başa çıkmak için gerekmektedir. Bunlar belirtilir stdin, stdoutve, stderrher zamanki gibi.
Şunları sağlayabilirsiniz:
None, akışı yeniden yönlendirmek istemediğinizi gösterir. Bunları her zamanki gibi miras alacaktır. POSIX sistemlerinde, bunun en azından Python'ları kullanacağı anlamına gelmediğini sys.stdout, sadece Python'un gerçek stdout'unu; sonunda demoya bakın.
- Bir
intdeğer. Bu bir "ham" dosya tanımlayıcıdır (en azından POSIX'te). (Yan not: PIPEve STDOUTaslında intdahili olarak, ancak -1 ve -2 "imkansız" tanımlayıcılar.)
- Bir akış — gerçekten,
filenoyöntemi olan herhangi bir nesne . Popenbu akış için tanımlayıcıyı bulur ve stream.fileno()bir intdeğerde olduğu gibi ilerler .
subprocess.PIPE, Python'un bir boru oluşturması gerektiğini gösterir.
subprocess.STDOUT( stderryalnızca): Python'a aynı tanımlayıcıyı kullanmasını söyleyin stdout. Eğer bir (non verdiyse Bu sadece mantıklı Noneiçin) değerini stdout, ve o zaman bile, sadece bir ihtiyaç ayarladığınız takdirde stdout=subprocess.PIPE. (Aksi takdirde, sağladığınız aynı argümanı sağlayabilirsiniz stdout, örneğin Popen(..., stdout=stream, stderr=stream).)
En kolay durumlar (boru yok)
Hiçbir şeyi yönlendirmezseniz (her üçünü de varsayılan Nonedeğer veya sarf malzemesi açık olarak bırakın None), Pipeoldukça kolaydır. Sadece alt süreci kapatmalı ve çalışmasına izin vermeli. Bir olmayan yönlendirmek Ya da, PIPE-bir intveya dere var fileno()OS tüm çalışmaları yaptığı gibi -it, kolay hala. Python, yalnızca stdin, stdout ve / veya stderr'sini sağlanan dosya tanımlayıcılarına bağlayarak alt işlemi kapatmalıdır.
Hala kolay kasa: bir boru
Yalnızca bir akışı yeniden yönlendirirseniz, Pipeyine de oldukça kolay şeyler vardır. Bir seferde bir akış seçip izleyelim.
Bazı tedarik istediğinizi varsayalım stdin, ama izin stdoutve stderrun-yönlendirildi gitmek veya bir dosya tanımlayıcı gidin. Üst işlem olarak, Python programınızın write()verileri kanaldan göndermek için kullanması yeterlidir . Bunu kendiniz yapabilirsiniz, örneğin:
proc = subprocess.Popen(cmd, stdin=subprocess.PIPE)
proc.stdin.write('here, have some data\n') # etc
veya stdin verilerini proc.communicate()daha sonra stdin.writeyukarıda gösterilen şekilde aktarabilirsiniz . Geri gelen çıktı yok, bu yüzden communicate()sadece bir gerçek iş var: aynı zamanda boruyu sizin için kapatır. (Arama yapmazsanız , boruyu kapatmak için proc.communicate()aramalısınız proc.stdin.close(), böylece alt süreç daha fazla veri olmadığını bilir.)
Eğer yakalama istediğinizi varsayalım stdoutama izinli stdinve stderryalnız. Yine, kolaydır: proc.stdout.read()daha fazla çıkış kalmayıncaya kadar arayın (veya eşdeğerini). Yana proc.stdout()normal Python I / O sizin gibi, üzerinde tüm normal yapıları kullanabilir akışı:
for line in proc.stdout:
veya, tekrar kullanabilirsiniz proc.communicate(), ki bu sadece read()sizin için yapar .
Yalnızca yakalamak istiyorsanız stderr, ile aynı şekilde çalışır stdout.
İşler zorlaşmadan önce bir numara daha var. Eğer yakalama istediğinizi varsayalım stdoutve aynı zamanda yakalamak stderrancak Stdout'a aynı boruya:
proc = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
Bu durumda, subprocess"hileler"! Bunu yapmak zorunda, bu yüzden gerçekten hile değil: hem stdout hem de stderr, üst (Python) sürecine geri beslenen (tekli) boru tanımlayıcıya yönlendirilen alt süreci başlatır. Üst tarafta yine çıktıyı okumak için yalnızca tek bir boru tanımlayıcı bulunur. Tüm "stderr" çıktıları görünür proc.stdoutve eğer ararsanız proc.communicate(), stderr sonucu (gruptaki ikinci değer) Nonedize olmaz.
Zor durumlar: iki veya daha fazla boru
En az iki boru kullanmak istediğinizde problemler ortaya çıkıyor. Aslında, subprocesskodun kendisi şu bite sahiptir:
def communicate(self, input=None):
...
# Optimization: If we are only using one pipe, or no pipe at
# all, using select() or threads is unnecessary.
if [self.stdin, self.stdout, self.stderr].count(None) >= 2:
Ama ne yazık ki, burada en az iki ve belki üç farklı boru yaptık, bu yüzden count(None)1 veya 0 döndürür. İşleri zor yoldan yapmalıyız.
Windows'ta, bu kullanımlar threading.Threadiçin birikir sonuçlarına self.stdoutve self.stderrve ana iş parçacığı teslim sahiptir self.stdin(daha sonra yakın ve boru) giriş verileri.
POSIX'te bu, pollmevcutsa, aksi takdirde select, çıktı biriktirmek ve stdin girişi iletmek için kullanır . Tüm bunlar (tek) üst işlem / iş parçacığında çalışır.
Kilitlenmeyi önlemek için iş parçacığı veya anket / seçim gereklidir. Örneğin, üç akışı da üç ayrı boruya yönlendirdiğimizi varsayalım. Ayrıca, yazma işlemi askıya alınmadan önce bir boruya ne kadar verinin doldurulabileceğine ilişkin küçük bir sınır olduğunu ve okuma işleminin boruyu diğer uçtan "temizlemesini" beklediğini varsayalım. Bu küçük limiti sadece gösterim amacıyla tek bir bayta ayarlayalım. (Sınırın bir bayttan çok daha büyük olması dışında, aslında işler böyle çalışır.)
Ebeveyn (Python) işlemi çalışır, byte-diyelim ki birkaç yazmaya Eğer 'go\n'üzere proc.stdin, ilk bayt gider ve daha sonra ikinci boru boşaltma, ilk bayt okumak için alt işlemi için bekleyen, askıya alma Python işlemi neden olur.
Bu arada, alt sürecin dostça bir "Merhaba! Panik Yapmayın!" selamlama. HStdout borusuna gider, ama eüst okumak için bekleyen, askıya almak neden olur Hstdout'u borusunu boşaltma.
Şimdi sıkıştık: Python süreci uyuyor, "git" diyerek bitirmeyi bekliyor ve alt süreç de uyuyor, "Merhaba! Panik yapmayın!" Demeyi bitirmeyi bekliyor.
subprocess.PopenKod parçacığı-ya-select / anket ile bu sorunu ortadan kaldırır. Baytlar boruların üzerinden geçebildiklerinde giderler. Yapamadıklarında, yalnızca bir iş parçacığı (tüm işlem değil) uyumak zorundadır - veya seçme / yoklama durumunda, Python işlemi aynı anda "yazabilir" veya "kullanılabilir veriler" için bekler, sürecin standartlarına yazar yalnızca yer olduğunda ve stdout ve / veya stderr değerini yalnızca veri hazır olduğunda okur. proc.communicate()Kodu (aslında _communicatekıllı vakalar işlendiği yerlerde) döner tüm Stdin verileri bir kez (varsa) gönderilmiş ve tüm stdout ve / veya stderr veri birikmiş edilmiştir.
Her ikisini de stdoutve stderriki farklı boruda (herhangi bir stdinyönlendirmeden bağımsız olarak ) okumak istiyorsanız, kilitlenmeden de kaçınmanız gerekir. Buradaki kilitlenme senaryosu farklıdır - alt süreç, stderrveri çekerken uzun süre bir şey yazdığında stdoutveya tam tersi olduğunda gerçekleşir - ancak hala oradadır.
Demo
Ben yönlendirilmemiş Python subprocesses temel stdout yazmak değil göstermek için söz verdi sys.stdout. İşte bazı kodlar:
from cStringIO import StringIO
import os
import subprocess
import sys
def show1():
print 'start show1'
save = sys.stdout
sys.stdout = StringIO()
print 'sys.stdout being buffered'
proc = subprocess.Popen(['echo', 'hello'])
proc.wait()
in_stdout = sys.stdout.getvalue()
sys.stdout = save
print 'in buffer:', in_stdout
def show2():
print 'start show2'
save = sys.stdout
sys.stdout = open(os.devnull, 'w')
print 'after redirect sys.stdout'
proc = subprocess.Popen(['echo', 'hello'])
proc.wait()
sys.stdout = save
show1()
show2()
Çalıştırıldığında:
$ python out.py
start show1
hello
in buffer: sys.stdout being buffered
start show2
hello
Eklediğinizde stdout=sys.stdout, ilk rutinin başarısız olacağını unutmayın , çünkü bir StringIOnesnede yok fileno. İkincisi, yeniden yönlendirildiğinden beri helloeklerseniz , atlar .stdout=sys.stdoutsys.stdoutos.devnull
(Eğer Python'un dosya tanımlayıcısı-1 yönlendirmek ise alt süreci olacak o yönlendirmeyi izleyin. open(os.devnull, 'w')Çağrı kimin akışı üretir fileno()2. büyüktür)
Popen.pollgibi kullanabilirsiniz .