Alt işlemde 'shell = True' ifadesinin gerçek anlamı


260

subprocessModül ile farklı süreçler arıyorum . Ancak bir sorum var.

Aşağıdaki kodlarda:

callProcess = subprocess.Popen(['ls', '-l'], shell=True)

ve

callProcess = subprocess.Popen(['ls', '-l']) # without shell

Her ikisi de çalışır. Dokümanları okuduktan sonra shell=True, kodun kabuktan yürütülmesi anlamına geldiğini öğrendim . Yani bu yokluğunda, sürecin doğrudan başlatıldığı anlamına gelir.

Benim durumum için neyi tercih etmeliyim - bir süreç çalıştırmam ve çıktısını almam gerekiyor. Kabuğun içinden veya dışından çağırmanın ne yararı var?


21
ilk komut yanlış: eğer Unix'teki program yerine (kabuk) 'a -liletilir . Dize bağımsız değişkeni , bir liste yerine çoğu durumda ile birlikte kullanılmalıdır . /bin/shlsshell=Trueshell=True
jfs

1
re "süreç doğrudan başlatıldı": Wut?
allyourcode

9
"İkisi de çalışır" ifadesi. Bu 2 çağrı hakkında yanlış ve yanıltıcı. Aramalar farklı çalışıyor. Sadece ve tersi arasında geçiş shell=Trueyapmak Falsebir hatadır. Gönderen docs : "kabuk = Doğru, (...) bağımsız değişken bir dizi ise, ilk öğe belirtir komut dizesi ve herhangi bir ek ürün kabuğun kendisine ek argüman olarak ele alınacaktır ile Açık POSIX'deki". Windows'da istenmeyen dönüşüm olabilecek otomatik dönüşüm var .
mbdevpl

Yanıtlar:


183

Kabuk üzerinden arama yapmamanın yararı, bir 'gizem programı' başlatmamanızdır. POSIX'te, ortam değişkeni SHELLhangi ikilinin "kabuk" olarak çağrıldığını denetler. Windows'da, bourne shell descendent yoktur, sadece cmd.exe.

Kabuğu çağırmak kullanıcının seçtiği bir programı çağırır ve platforma bağlıdır. Genel olarak konuşursak, kabuk yoluyla istilalardan kaçının.

Kabuk üzerinden çağırmak, ortam değişkenlerini ve dosya globlarını kabuğun olağan mekanizmasına göre genişletmenize izin verir. POSIX sistemlerinde, kabuk dosya kürelerini bir dosya listesine genişletir. Windows dosya topak (örneğin, "*. *") Üzerinde zaten kabuk tarafından genişletilmediğinde (ancak bir komut satırında ortam değişkenleri olan cmd.exe tarafından genişletilmiş).

Ortam değişkeni genişletmeleri ve dosya globları istediğinizi düşünüyorsanız ILS, alt program çağırmalarını kabuk üzerinden gerçekleştiren ağ hizmetlerine 1992 ish saldırılarını araştırın . Örnekler arasında çeşitli sendmailarka kapılar yer alır ILS.

Özet olarak, kullanın shell=False.


2
Cevap için teşekkürler. Gerçekten de istismarlar hakkında endişelenmem gereken aşamada değilim, ama ne elde ettiğinizi anlıyorum.
user225312

55
Başlangıçta dikkatsizseniz, daha sonra endişelenmenize yardımcı olmayacaktır. ;)
Heath Hunnicutt

Alt işlemin maksimum belleğini sınırlamak isterseniz ne olur? stackoverflow.com/questions/3172470/…
Pramod

8
hakkında ifade $SHELLdoğru değil. Subprocess.html'den alıntı yapmak için: "Unix ile shell=Truekabuk varsayılan olarak kullanılır /bin/sh." (değil $SHELL)
marcin

1
@ user2428107: Evet, Perl'de backtick invocation kullanıyorsanız, kabuk invocation kullanıyorsunuz ve aynı sorunları açıyorsunuz. openBir programı çağırmanın ve çıktıyı yakalamanın güvenli yollarını istiyorsanız 3+ arg parametresini kullanın .
ShadowRanger

138
>>> import subprocess
>>> subprocess.call('echo $HOME')
Traceback (most recent call last):
...
OSError: [Errno 2] No such file or directory
>>>
>>> subprocess.call('echo $HOME', shell=True)
/user/khong
0

Kabuk bağımsız değişkenini gerçek bir değere ayarlamak, alt işlemin bir ara kabuk işlemi oluşturmasına ve komutu çalıştırmasını bildirmesine neden olur. Başka bir deyişle, bir ara kabuk kullanmak, komut dizesindeki değişkenlerin, glob kalıplarının ve diğer özel kabuk özelliklerinin komut çalıştırılmadan önce işlendiği anlamına gelir. Burada, örnekte $ HOME, echo komutundan önce işlenmiştir. Aslında bu, ls -l komutu basit bir komut olarak kabul edilirken, kabuk genişletme komutudur.

Kaynak: Alt İşlem Modülü


16
Bunun neden seçilen cevap olmadığını bilmiyorum. Aslında soruyla eşleşen kişi
Rodrigo Lopez Guerra

1
Katılıyorum. bu benim için shell = True'nun ne anlama geldiğini anlamak için iyi bir örnek.
user389955

2
Kabuk bağımsız değişkenini gerçek bir değere ayarlamak, alt işlemin bir ara kabuk işlemi başlatmasına neden olur ve bunu komutun çalıştırmasını söyler. Bu cevap neden kabul edilmiyor ??? neden?
pouya

Sorunun ilk argüman bir dize değil, bir liste olduğunu düşünüyorum, ama kabuk yanlış ise hata veriyor. Komutun bir listeye değiştirilmesi bu çalışmayı sağlayacaktır
Lincoln Randall McFarland

Üzgünüm önceki yorumum bitmeden gitti. Açıkça söylemek gerekirse: shell = True ile alt işlem kullanımını görüyorum ve komut bir dizedir, örneğin 'ls -l', (Bu hatayı önlemeyi bekliyorum), ancak alt işlem bir liste (ve bir öğe listesi olarak bir dize) alıyor . Bir kabuk çağırmakla (ve bununla ilgili güvenlik sorunlarıyla ) çalıştırmak için bir liste subprocess.call (['ls', '-l']) kullanın
Lincoln Randall McFarland

42

Shell = True ile işlerin yanlış gidebileceği bir örnek burada gösterilmektedir

>>> from subprocess import call
>>> filename = input("What file would you like to display?\n")
What file would you like to display?
non_existent; rm -rf / # THIS WILL DELETE EVERYTHING IN ROOT PARTITION!!!
>>> call("cat " + filename, shell=True) # Uh-oh. This will end badly...

Dokümanı buradan kontrol edin: subprocess.call ()


6
Bağlantı çok faydalı. Bağlantıda belirtildiği gibi: Güvenilmeyen bir girdiden onaylanmamış girdi içeren kabuk komutlarının yürütülmesi, bir programı kabuk enjeksiyonuna karşı savunmasız hale getirir; bu, rasgele komut yürütmesine neden olabilecek ciddi bir güvenlik açığıdır. Bu nedenle, komut dizesinin harici girdiden oluşturulduğu durumlarda shell = True kullanımı kesinlikle önerilmez.
jtuki

39

Programların kabuk üzerinden yürütülmesi, programa iletilen tüm kullanıcı girdilerinin, çağrılan kabuğun sözdizimi ve semantik kurallarına göre yorumlanması anlamına gelir. En iyi ihtimalle, bu yalnızca kullanıcıya rahatsızlık verir, çünkü kullanıcının bu kurallara uyması gerekir. Örneğin, tırnak işaretleri veya boşluklar gibi özel kabuk karakterleri içeren yollardan kaçılmalıdır. En kötüsü, kullanıcı rastgele programlar yürütebileceğinden güvenlik sızıntılarına neden olur.

shell=Truekelime bölme veya parametre genişletme gibi belirli kabuk özelliklerinden yararlanmak için bazen uygundur. Ancak, böyle bir özellik gerekliyse, size diğer modülleri kullanın (örn os.path.expandvars(). Parametre genişletme veya shlexkelime bölme için). Bu daha fazla iş anlamına gelir, ancak diğer sorunlardan kaçınır.

Kısacası: Kesinlikle kaçının shell=True.


16

Buradaki diğer cevaplar, subprocessbelgelerde de belirtilen güvenlik uyarılarını yeterince açıklamaktadır . Ancak buna ek olarak, çalıştırmak istediğiniz programı başlatmak için bir kabuk başlatmanın yükü, kabuğun işlevselliğini gerçekten kullanmadığınız durumlar için genellikle gereksizdir ve kesinlikle saçmadır. Ayrıca, ek gizli karmaşıklık sizi korkutmalıdır, özellikle kabuk veya sağladığı hizmetlere aşina .

Kabuk ile etkileşimlerin önemsiz olduğu durumlarda, artık Python betiğinin (gelecekteki kendiniz olabilir veya olmayabilir) okuyucu ve bakıcısının hem Python hem de kabuk betiğini anlaması gerekir. Python'un "açık, örtük olmaktan daha iyidir" sloganını hatırlayın ;Python kodu, eşdeğer (ve genellikle çok kısa) kabuk komut dosyasından biraz daha karmaşık olsa bile, kabuğu kaldırmak ve işlevselliği yerel Python yapılarıyla değiştirmek daha iyi olabilir. Harici bir süreçte yapılan işi en aza indirmek ve kontrolü kendi kodunuz dahilinde olabildiğince uzak tutmak genellikle iyi bir fikirdir, çünkü sadece görünürlüğü geliştirir ve istenen veya istenmeyen yan etki risklerini azaltır.

Joker karakter genişletmesi, değişken enterpolasyon ve yeniden yönlendirme, yerel Python yapılarıyla kolayca değiştirilebilir. Parçaların veya hepsinin Python'da makul bir şekilde yeniden yazılamadığı karmaşık bir kabuk boru hattı, belki de kabuğu kullanmayı düşünebileceğiniz tek durum olacaktır. Yine de performans ve güvenlik sonuçlarını anladığınızdan emin olmalısınız.

Önemsiz durumda, önlemek için shell=True, sadece değiştirin

subprocess.Popen("command -with -options 'like this' and\\ an\\ argument", shell=True)

ile

subprocess.Popen(['command', '-with','-options', 'like this', 'and an argument'])

İlk argümanın execvp()nasıl iletileceği dizelerin bir listesi olduğuna ve dizelerden ve ters eğik çizgiden kaçan kabuk metakarakterlerinin genellikle nasıl gerekli olmadığına (veya yararlı veya doğru) dikkat edin. Belki de bkz. Tırnaklar ne zaman bir kabuk değişkeni etrafına sarılır?

Bir yana, paketteki Popendaha basit sarmalayıcılardan birinin istediğiniz şeyi yapmasından kaçınmak subprocessistersiniz. Yeterince yeni bir Python'unuz varsa, muhtemelen kullanmalısınız subprocess.run.

  • Çalıştırdığınız check=Truekomut başarısız olursa başarısız olur.
  • Bununla stdout=subprocess.PIPEbirlikte komutun çıktısını yakalar.
  • Biraz belirsiz, universal_newlines=True çıktıyı uygun bir Unicode dizgisine deşifre eder (sadece bytessistem kodlamasını başka türlü Python 3'te).

Değilse, birçok görev için istediğiniz check_output başarılı check_callolup olmadığını kontrol ederken veya toplanacak çıktı yoksa, bir komuttan çıktı almak .

David Korn'dan bir alıntıyla kapatacağım: "Taşınabilir bir kabuk yazmak, taşınabilir bir kabuk komut dosyasından daha kolaydır." subprocess.run('echo "$HOME"', shell=True)Windows için bile taşınabilir değildir.


Teklifin Larry Wall'dan geldiğini sanıyordum ama Google bana aksini söylüyor.
Üçlü

Bu yüksek konuşma - ancak değiştirme için teknik bir öneri yok: Burada, OS-X'de, 'open' ile başlattığım bir Mac Uygulaması'nın pid'ini almaya çalışıyorum: process = subprocess.Popen ('/ usr / bin / pgrep - n '+ app_name, shell = False, stdout = subprocess.PIPE, stderr = subprocess.PIPE) app_pid, err = process.communicate () --- ama shell = True kullanmazsam çalışmaz. Şimdi ne olacak?
Motti Shneor

Birçoğu mükemmel cevaplarla nasıl önleneceğine dair bir sürü soru var shell=True. Sen neden olanı seçtin yerine.
tripleee

@MottiShneor Geri bildiriminiz için teşekkür ederiz; basit örnek eklendi
tripleee

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.