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.)
Popen
Fonksiyon biraz aynı zamanda sıfır-üç I / O akışları ile başa çıkmak için gerekmektedir. Bunlar belirtilir stdin
, stdout
ve, stderr
her 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
int
değer. Bu bir "ham" dosya tanımlayıcıdır (en azından POSIX'te). (Yan not: PIPE
ve STDOUT
aslında int
dahili olarak, ancak -1 ve -2 "imkansız" tanımlayıcılar.)
- Bir akış — gerçekten,
fileno
yöntemi olan herhangi bir nesne . Popen
bu akış için tanımlayıcıyı bulur ve stream.fileno()
bir int
değerde olduğu gibi ilerler .
subprocess.PIPE
, Python'un bir boru oluşturması gerektiğini gösterir.
subprocess.STDOUT
( stderr
yalnızca): Python'a aynı tanımlayıcıyı kullanmasını söyleyin stdout
. Eğer bir (non verdiyse Bu sadece mantıklı None
iç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 None
değer veya sarf malzemesi açık olarak bırakın None
), Pipe
oldukça kolaydır. Sadece alt süreci kapatmalı ve çalışmasına izin vermeli. Bir olmayan yönlendirmek Ya da, PIPE
-bir int
veya 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, Pipe
yine de oldukça kolay şeyler vardır. Bir seferde bir akış seçip izleyelim.
Bazı tedarik istediğinizi varsayalım stdin
, ama izin stdout
ve stderr
un-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.write
yukarı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 stdout
ama izinli stdin
ve stderr
yalnı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 stdout
ve aynı zamanda yakalamak stderr
ancak 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.stdout
ve eğer ararsanız proc.communicate()
, stderr sonucu (gruptaki ikinci değer) None
dize olmaz.
Zor durumlar: iki veya daha fazla boru
En az iki boru kullanmak istediğinizde problemler ortaya çıkıyor. Aslında, subprocess
kodun 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.Thread
için birikir sonuçlarına self.stdout
ve self.stderr
ve ana iş parçacığı teslim sahiptir self.stdin
(daha sonra yakın ve boru) giriş verileri.
POSIX'te bu, poll
mevcutsa, 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. H
Stdout borusuna gider, ama e
üst okumak için bekleyen, askıya almak neden olur H
stdout'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.Popen
Kod 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 _communicate
kı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 stdout
ve stderr
iki farklı boruda (herhangi bir stdin
yönlendirmeden bağımsız olarak ) okumak istiyorsanız, kilitlenmeden de kaçınmanız gerekir. Buradaki kilitlenme senaryosu farklıdır - alt süreç, stderr
veri çekerken uzun süre bir şey yazdığında stdout
veya tam tersi olduğunda gerçekleşir - ancak hala oradadır.
Demo
Ben yönlendirilmemiş Python subprocess
es 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 StringIO
nesnede yok fileno
. İkincisi, yeniden yönlendirildiğinden beri hello
eklerseniz , atlar .stdout=sys.stdout
sys.stdout
os.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.poll
gibi kullanabilirsiniz .