Buradaki önceki yanıtları biraz genişletmek için, genellikle gözden kaçan bazı ayrıntılar vardır.
- Tercih
subprocess.run()
üzerinde subprocess.check_call()
üzerinde ve arkadaşlar subprocess.call()
üzerinde subprocess.Popen()
üzerinde os.system()
üzerindeos.popen()
- Anlayın ve muhtemelen kullanın
text=True
, aka universal_newlines=True
.
- Teklifin anlamını
shell=True
veya shell=False
nasıl değiştiğini ve kabuk kolaylıklarının kullanılabilirliğini anlayın .
sh
Ve Bash arasındaki farkları anlama
- Bir alt işlemin üst öğeden nasıl ayrı olduğunu ve genellikle üst öğeyi değiştiremediğini anlayın.
- Python yorumlayıcısını Python'un bir alt süreci olarak çalıştırmaktan kaçının.
Bu konular aşağıda daha ayrıntılı olarak ele alınmaktadır.
Tercih subprocess.run()
veyasubprocess.check_call()
subprocess.Popen()
Fonksiyon düşük seviye beygir ancak doğru kullanımı zor ve rahatlıkla zaten çeşitli amaçlar için fonksiyonları sarıcı üst düzeyde bir dizi gibi standart kütüphanesinde bulunması kod birden fazla satır ... yapıştırarak / kopyalama sona, bunlar aşağıda daha ayrıntılı olarak sunulmuştur.
İşte belgelerden bir paragraf :
Alt süreçleri çağırmak için önerilen yaklaşım, run()
işlevini, işleyebileceği tüm kullanım durumları için kullanmaktır. Daha gelişmiş kullanım durumları için, alttaki Popen
arayüz doğrudan kullanılabilir.
Ne yazık ki, bu sarmalayıcı işlevlerinin kullanılabilirliği Python sürümleri arasında farklılık gösterir.
subprocess.run()
Python 3.5'te resmen tanıtıldı. Aşağıdakilerin tümünü değiştirmek içindir.
subprocess.check_output()
Python 2.7 / 3.1'de tanıtıldı. Temel olaraksubprocess.run(..., check=True, stdout=subprocess.PIPE).stdout
subprocess.check_call()
Python 2.5'te tanıtıldı. Temel olaraksubprocess.run(..., check=True)
subprocess.call()
Python 2.4'te orijinal subprocess
modülde ( PEP-324 ) tanıtıldı . Temel olaraksubprocess.run(...).returncode
Yüksek seviye API vs subprocess.Popen()
Yeniden düzenlenmiş ve genişletilmiş subprocess.run()
, yerini aldığı eski eski işlevlerden daha mantıklı ve çok yönlüdür. CompletedProcess
Bitmiş alt işlemden çıkış durumunu, standart çıktıyı ve diğer birkaç sonucu ve durum göstergesini almanıza izin veren çeşitli yöntemlere sahip bir nesne döndürür .
subprocess.run()
kontrolü çalıştırmak ve Python'a geri göndermek için bir programa ihtiyacınız varsa gitmenin yoludur. Daha fazla ilgili senaryolar için (arka plan süreçleri, belki de Python üst programı ile etkileşimli G / Ç ile) hala subprocess.Popen()
tüm tesisatları kendiniz kullanmanız ve bunlarla ilgilenmeniz gerekir . Bu, tüm hareketli parçaların oldukça karmaşık bir şekilde anlaşılmasını gerektirir ve hafife alınmamalıdır. Daha basit Popen
nesne , alt işlemin kalan süresi boyunca kodunuzdan yönetilmesi gereken (muhtemelen hala çalışıyor) işlemi temsil eder.
Belki de subprocess.Popen()
sadece bir süreç yarattığı vurgulanmalıdır . Eğer bunu bırakırsanız, Python ile eşzamanlı olarak çalışan bir alt işleminiz olur, böylece bir "arka plan" işlemi gerçekleşir. Girdi veya çıktı yapması veya sizinle başka bir şekilde koordinasyon yapması gerekmiyorsa, Python programınıza paralel olarak faydalı işler yapabilir.
Kaçının os.system()
veos.popen()
Sonsuz zamandan beri (iyi, Python 2.5 beri) os
modülü belgelerine tercih öneri içeriyordu etmiştir subprocess
üzerinde os.system()
:
subprocess
Modül yeni süreçler yumurtlama ve onların sonuçlarını almak için, daha güçlü olanaklar sağlar; bu modülü kullanmak bu işlevi kullanmak için tercih edilir.
Sorunları, system()
sisteme bağlı olduğu ve alt süreçle etkileşime geçmenin yolları sunmadığıdır. Python'un erişemeyeceği standart çıkış ve standart hata ile çalışır. Python'un geri aldığı tek bilgi, komutun çıkış durumudur (sıfır, başarı anlamına gelir, ancak sıfır olmayan değerlerin anlamı da sisteme bağımlıdır).
PEP-324 (daha önce bahsedilmişti), neden os.system
sorunlu olduğu ve subprocess
bu sorunları nasıl çözme girişimleri için daha ayrıntılı bir gerekçe içeriyor .
os.popen()
eskiden daha güçlü bir şekilde cesareti kırılmıştı :
2.6 sürümünden beri kullanımdan kaldırıldı : Bu işlev kullanılmıyor. subprocess
Modülü kullanın .
Bununla birlikte, Python 3'te bir zamandan beri, basitçe kullanmak için yeniden uygulandı subprocess
ve subprocess.Popen()
ayrıntılar için belgelere yönlendirildi .
Anlayın ve genellikle kullanın check=True
subprocess.call()
Aynı sınırlamaların çoğuna sahip olduğunu da fark edeceksiniz os.system()
. Düzenli kullanımda, genellikle sürecin başarılı bir şekilde tamamlanıp tamamlanmadığını kontrol etmelisiniz subprocess.check_call()
ve hangisi subprocess.check_output()
(burada ikinci aşama da bitmiş alt sürecin standart çıktısını döndürür). Benzer şekilde, alt işlemin bir hata durumu döndürmesine özellikle izin vermeniz gerekmedikçe genellikle check=True
ile birlikte kullanmalısınız subprocess.run()
.
Uygulamada, check=True
veya subprocess.check_*
ile Python , alt işlem sıfır olmayan bir çıkış durumu döndürürse bir CalledProcessError
istisna atar .
İle ortak bir hata , alt işlem başarısız olursa, aşağı akış kodu başarısız olduğunda subprocess.run()
atlamak check=True
ve şaşırtmaktır.
Öte yandan, ortak bir sorun check_call()
ve check_output()
istisnası örneğin yükseltildi zaman gözü kapalı bu işlevleri kullanılan kullanıcılar şaşırdık olmasıydı grep
bir maç bulamadık. (Muhtemelen grep
aşağıda açıklandığı gibi yerel Python koduyla değiştirmelisiniz .)
Her şey sayılır, kabuk komutlarının bir çıkış kodunu nasıl döndürdüğünü ve hangi koşullar altında sıfırdan farklı (hata) bir çıkış kodu döndüreceklerini anlamanız ve tam olarak nasıl işlenmesi gerektiğine dair bilinçli bir karar vermeniz gerekir.
Anlayın ve muhtemelen text=True
aka kullanınuniversal_newlines=True
Python 3'ten beri, Python'un içindeki dizeler Unicode dizeleridir. Ancak bir alt işlemin Unicode çıktısı veya dizeleri oluşturduğunun garantisi yoktur.
(Farklılıklar hemen belli değilse, Ned Batchelder'in Pragmatic Unicode'u , açıkça zorunlu değilse, okuma önerilir. İsterseniz bağlantıyı arkasında 36 dakikalık bir video sunumu olsa da, sayfayı okumak kendiniz önemli ölçüde daha az zaman alacaktır. )
Derinlerde, Python bir bytes
tampon getirmeli ve bir şekilde yorumlamalıdır. O ikili verilerin bir damla içeriyorsa, bu olmamalı bu hataya açık ve hata neden olan davranış, çünkü Unicode dizesine deşifre edilmesi - Birçok Python 2 komut kalbura sinir bozucu davranışının hassas tür bir yolu yoktu önce kodlanmış metin ve ikili veriler arasında doğru bir şekilde ayrım yapın.
İle text=True
, Python'a, aslında, sistemin varsayılan kodlamasında metinsel verileri beklediğinizi ve Python'un yeteneğinin en iyisi olan bir Python (Unicode) dizgisine kodunun çözülmesi gerektiğini söylersiniz (genellikle herhangi bir ortama kadar UTF-8) tarih sistemi, belki Windows hariç?)
Geri talep ettiğiniz şey bu değilse , Python size ve dizelerinde sadece bytes
dizeler verecektir . Belki bazı daha sonra işaret yok onlar metin dizeleri sonuçta olduklarını biliyoruz ve onların kodlama biliyorum. Sonra bunları deşifre edebilirsiniz.stdout
stderr
normal = subprocess.run([external, arg],
stdout=subprocess.PIPE, stderr=subprocess.PIPE,
check=True,
text=True)
print(normal.stdout)
convoluted = subprocess.run([external, arg],
stdout=subprocess.PIPE, stderr=subprocess.PIPE,
check=True)
# You have to know (or guess) the encoding
print(convoluted.stdout.decode('utf-8'))
Python 3.7, daha text
önce biraz yanıltıcı olarak adlandırılan anahtar kelime argümanı için daha kısa ve daha açıklayıcı ve anlaşılır takma adı tanıttı universal_newlines
.
Anlamak shell=True
vsshell=False
İle shell=True
size sizin kabuğuna tek dizesi geçirmek ve kabuk oradan alır.
İle shell=False
size kabuk atlayarak OS bağımsız değişkenlerin bir listesini geçmektedir.
Bir kabuğunuz yoksa, bir işlemi kaydedersiniz ve hata veya hatta güvenlik sorunları barındırabilen veya barındırmayan oldukça önemli miktarda gizli karmaşıklıktan kurtulursunuz .
Öte yandan, bir kabuğunuz yoksa, yeniden yönlendirme, joker karakter genişletmesi, iş kontrolü ve çok sayıda diğer kabuk özelliğiniz yoktur.
Yaygın bir hata, shell=True
Python'u bir jeton listesi kullanmak ve yine de iletmektir. Bu bazı durumlarda işe yarar, ancak gerçekten kötü tanımlanmış ve ilginç şekillerde kırılabilir.
# XXX AVOID THIS BUG
buggy = subprocess.run('dig +short stackoverflow.com')
# XXX AVOID THIS BUG TOO
broken = subprocess.run(['dig', '+short', 'stackoverflow.com'],
shell=True)
# XXX DEFINITELY AVOID THIS
pathological = subprocess.run(['dig +short stackoverflow.com'],
shell=True)
correct = subprocess.run(['dig', '+short', 'stackoverflow.com'],
# Probably don't forget these, too
check=True, text=True)
# XXX Probably better avoid shell=True
# but this is nominally correct
fixed_but_fugly = subprocess.run('dig +short stackoverflow.com',
shell=True,
# Probably don't forget these, too
check=True, text=True)
Hangi koşullar altında çalışmayı durdurabileceğini tam olarak anlamadığınız sürece ortak imbik "ama benim için çalışıyor" yararlı bir çürütme değil.
Yeniden Düzenleme Örneği
Çoğu zaman, kabuğun özellikleri yerel Python kodu ile değiştirilebilir. Basit Awk veya sed
komut dosyaları muhtemelen Python'a çevrilmelidir.
Bunu kısmen göstermek için, burada birçok kabuk özelliği içeren tipik ama biraz aptalca bir örnek var.
cmd = '''while read -r x;
do ping -c 3 "$x" | grep 'round-trip min/avg/max'
done <hosts.txt'''
# Trivial but horrible
results = subprocess.run(
cmd, shell=True, universal_newlines=True, check=True)
print(results.stdout)
# Reimplement with shell=False
with open('hosts.txt') as hosts:
for host in hosts:
host = host.rstrip('\n') # drop newline
ping = subprocess.run(
['ping', '-c', '3', host],
text=True,
stdout=subprocess.PIPE,
check=True)
for line in ping.stdout.split('\n'):
if 'round-trip min/avg/max' in line:
print('{}: {}'.format(host, line))
Burada dikkat edilmesi gereken bazı noktalar:
- İle
shell=False
kabuk dizeleri etrafında gerektirdiğini size alıntı gerekmez. Zaten tırnak koymak muhtemelen bir hatadır.
- Bir alt işlemde mümkün olduğunca az kod çalıştırmak genellikle mantıklıdır. Bu size Python kodunuzdan yürütme üzerinde daha fazla kontrol sağlar.
- Bunu söyledikten sonra, karmaşık kabuk boru hatları sıkıcıdır ve bazen Python'da yeniden uygulanması zorlaşır.
Yeniden düzenlenmiş kod ayrıca kabuğun çok kısa bir sözdizimi ile sizin için ne kadar başarılı olduğunu da gösterir - daha iyi veya daha kötü. Python diyor örtülü daha iyidir açık ama Python kodu olan oldukça ayrıntılı ve belki bu gerçekten olduğundan daha karmaşık görünüyor. Öte yandan, kabuk komut çıktısı ile birlikte ana bilgisayar adını kolayca ekleyebileceğimiz geliştirme ile önemsiz bir şekilde örneklendiği gibi, başka bir şeyin ortasında kontrol sahibi olabileceğiniz bir dizi nokta sunar. (Bu hiçbir şekilde kabukta yapmak zor değildir, ama yine de başka bir saptırma ve belki de başka bir süreç pahasına.)
Ortak Kabuk Yapılar
Tamlık için, bu kabuk özelliklerinden bazılarının kısa açıklamaları ve bunların yerel Python olanaklarıyla nasıl değiştirilebileceğine dair bazı notlar.
- Globbing olarak adlandırılan joker karakter genişletmesi,
glob.glob()
basit Python dizesi karşılaştırmalarıyla veya çok sık olarak değiştirilebilir for file in os.listdir('.'): if not file.endswith('.png'): continue
. Bash, .{png,jpg}
parantez genişletme ve {1..100}
tilde genişletme gibi çeşitli genişletme olanaklarına sahiptir ( ~
ana dizininize ve daha genel ~account
olarak başka bir kullanıcının ana dizinine genişler )
- Kabuk değişkenleri gibi
$SHELL
veya $my_exported_var
bazen Python değişkenleri ile değiştirilebilir. Örneğin olarak dışa kabuk değişkenleri mevcuttur os.environ['SHELL']
(anlamı export
subprocesses değişken kullanılabilir olmasını sağlamaktır -. Açıkça Python kabuğun alt işlemi ya da tam tersi olarak çalışan mevcut olmayacaktır subprocesses kullanılamaz bir değişken env=
kelime subprocess
yöntemlere argüman , alt işlem ortamını sözlük olarak tanımlamanızı sağlar, bu nedenle Python değişkenini bir alt işlem için görünür hale getirmenin bir yolu budur). İle shell=False
herhangi bir tırnak kaldırmak için nasıl anlamak gerekir; örneğin, dizin adı etrafında tırnak işaretleri olmadan cd "$HOME"
eşdeğerdir os.chdir(os.environ['HOME'])
. (Çok sıkcd
yine de yararlı veya gerekli değildir ve birçok yeni başlayan değişkenin etrafındaki çift tırnak işaretlerini atlar ve bir güne kadar ondan uzaklaşır ... )
- Yeniden yönlendirme, bir dosyadan standart girdiniz olarak okumanıza ve standart çıktınızı bir dosyaya yazmanıza olanak tanır.
grep 'foo' <inputfile >outputfile
Açılan outputfile
yazma ve inputfile
okuma ve standart girdi olarak içeriğini geçer grep
olan standart çıktı daha sonra topraklarda, outputfile
. Bunun yerine yerel Python kodu ile değiştirilmesi genellikle zor değildir.
- Boru hatları bir yönlendirme şeklidir.
echo foo | nl
standart çıktısının echo
standart girdi olduğu iki alt işlem çalıştırır nl
(işletim sistemi düzeyinde, Unix benzeri sistemlerde, bu tek bir dosya tanıtıcısıdır). Boru hattının bir veya iki ucunu yerel Python kodu ile değiştiremiyorsanız, sonuçta özellikle bir kabuk kullanmanın iki veya üçten fazla işlemi varsa ( pipes
Python standart kütüphanesindeki modüle veya bir sayıya bakın) daha modern ve çok yönlü üçüncü taraf rakiplerden).
- İş kontrolü, işleri kesmenize, arka planda çalıştırmanıza, ön plana döndürmenize vb. Olanak tanır. Bir işlemi durdurmak ve devam ettirmek için temel Unix sinyalleri elbette Python'dan da temin edilebilir. Ancak işler, Python'dan böyle bir şey yapmak isteyip istemediğinizi anlamak zorunda olduğunuz süreç gruplarını vb.İçleyen kabukta daha üst düzey bir soyutlamadır.
- Kabukta alıntı yapmak, her şeyin temelde bir dize olduğunu anlayana kadar potansiyel olarak kafa karıştırıcıdır . Buna
ls -l /
eşdeğerdir, 'ls' '-l' '/'
ancak değişmez değerler arasında alıntı yapmak tamamen isteğe bağlıdır. Kabuk meta karakterleri içeren tırnaksız dizeler parametre genişletmesi, boşluk belirteci ve joker karakter genişletmesi geçirir; çift tırnak, boşluk belirtecini ve joker karakter genişlemesini engeller, ancak parametre genişletmelerine izin verir (değişken değiştirme, komut değiştirme ve ters eğik çizgi işleme). Bu teoride basittir, ancak özellikle birkaç yorum katmanı olduğunda (örneğin bir uzak kabuk komutu) şaşırtıcı olabilir.
sh
Ve Bash arasındaki farkları anlama
subprocess
/bin/sh
özellikle başka bir şekilde talep etmediğiniz sürece kabuk komutlarınızı çalıştırır (tabii ki COMSPEC
değişkenin değerini kullandığı Windows'ta ). O Bu araçlar çeşitli Bash okunur diziler gibi özellikler, [[
vb kullanılamaz.
Yalnızca Bash sözdizimini kullanmanız gerekiyorsa, kabuğa giden yolu şu şekilde aktarabilirsiniz executable='/bin/bash'
(elbette Bash başka bir yere kuruluysa, yolu ayarlamanız gerekir).
subprocess.run('''
# This for loop syntax is Bash only
for((i=1;i<=$#;i++)); do
# Arrays are Bash-only
array[i]+=123
done''',
shell=True, check=True,
executable='/bin/bash')
A subprocess
, üst öğesinden ayrıdır ve değiştirilemez
Biraz yaygın bir hata,
subprocess.run('foo=bar', shell=True)
subprocess.run('echo "$foo"', shell=True) # Doesn't work
zerafet eksikliğinin yanı sıra, "alt süreç" adının "alt" kısmının anlaşılmasında da temel bir eksikliğe ihanet eder.
Bir alt süreç Python'dan tamamen ayrı çalışır ve bittiğinde, Python'un ne yaptığına dair hiçbir fikri yoktur (çıkış durumundan ve alt süreçten çıktından çıkarabileceği belirsiz göstergelerin dışında). Çocuk genellikle ebeveynin çevresini değiştiremez; bir değişken ayarlayamaz, çalışma dizinini değiştiremez veya pek çok kelimeyle, üst öğeden işbirliği olmadan üst öğesiyle iletişim kuramaz.
Bu özel durumda acil düzeltme, her iki komutu tek bir alt işlemde çalıştırmaktır;
subprocess.run('foo=bar; echo "$foo"', shell=True)
açıkçası bu özel kullanım durumu kabuk gerektirmez. Unutmayın, mevcut sürecin ortamını (ve böylece çocuklarını)
os.environ['foo'] = 'bar'
veya bir ortam ayarını
subprocess.run('echo "$foo"', shell=True, env={'foo': 'bar'})
(bariz yeniden düzenlemeden bahsetmiyorum subprocess.run(['echo', 'bar'])
; ama echo
elbette bir alt süreçte çalıştırılacak bir şeyin kötü bir örneğidir).
Python'u Python'dan çalıştırmayın
Bu biraz şüpheli bir tavsiye; kesinlikle Python yorumlayıcısını bir Python betiğinden bir alt işlem olarak çalıştırmanın mantıklı olduğu veya hatta mutlak bir gereklilik olduğu durumlar vardır. Ancak çok sık, doğru yaklaşım sadece import
diğer Python modülüne çağrı kodunuza girer ve işlevlerini doğrudan çağırır.
Diğer Python betiği kontrolünüz altındaysa ve bir modül değilse, bir tanesine dönüştürmeyi düşünün . (Bu cevap zaten çok uzun, bu yüzden burada ayrıntılara girmeyeceğim.)
Paralelliğe ihtiyacınız varsa, Python işlevlerini alt işlemlerde multiprocessing
modülle çalıştırabilirsiniz. threading
Tek bir işlemde birden çok görevi yürüten de vardır (bu daha hafiftir ve size daha fazla kontrol sağlar, ancak aynı zamanda bir işlem içindeki iş parçacıklarının sıkıca bağlı ve tek bir GIL'ye bağlı olmasıyla daha kısıtlıdır .)
cwm
. Belki de.bashrc
interaktif bash kullanımı için ortamı ayarlayan bazı yapılandırmalarınız var ?