Neden kabuk komutlarını doğrudan yürütmek yerine Python'un os modülü yöntemlerini kullanmalıyım?


157

Böyle, dosya / dizin oluşturma dosya özelliklerini değiştirerek, vb yerine sadece üzerinden bu komutları çalıştırarak olarak OS özgü görevleri yürütmek için Python'un kütüphane fonksiyonlarını kullanarak arkasındaki motivasyon nedir anlamaya çalışıyorum os.system()ya subprocess.call()?

Örneğin, neden os.chmodyapmak yerine kullanmak isteyeyim os.system("chmod...")?

Sadece kabuk komutlarını doğrudan yürütmek yerine mümkün olduğunca Python'un kullanılabilir kitaplık yöntemlerini kullanmanın daha "pitonik" olduğunu anlıyorum. Ancak, bunu işlevsellik açısından yapmanın arkasında başka bir motivasyon var mı?

Burada sadece basit tek satır kabuk komutlarını yürütmekten bahsediyorum. Görevin yürütülmesi üzerinde daha fazla kontrole ihtiyaç duyduğumuzda subprocess, örneğin modül kullanmanın daha mantıklı olduğunu anlıyorum .


6
Temelde kafadaki çiviye vurdun. Bahsettiğiniz işletim sistemi düzeyindeki görevler, yalnızca os.system yoluyla çağrılmak yerine kendi işlevlerini garanti edecek kadar yaygındır.
deweyredman

7
BTW, çalıştırma zamanını denediniz mi - os.chmod ve os.system ("chmod ...") . Sorunuzun bir kısmına cevap vereceğini tahmin ediyorum.
volkan

61
Neden printyapabildin os.system("echo Hello world!")?
user253751

25
Aynı nedenden dolayı os.pathyolları elle işlemek yerine işlemek için kullanmalısınız : çalıştığı her işletim sisteminde çalışır.
Bakuriu

51
"Kabuk komutlarını doğrudan yürütmek" aslında daha az doğrudan. Kabuk, sisteme düşük düzeyli bir arayüz os.chmoddeğildir chmodve kabuğun yapacağı programı çağırmaz . Kullanıldığında os.system('chmod ...'), C chmodişlevine çağrı yapmak için başka bir yürütülebilir dosyayı çağırmak üzere bir dizeyi yorumlamak için bir kabuk başlatılırken C os.chmod(...)doğrudan çok daha doğrudan gider chmod.
user2357112 Monica

Yanıtlar:


325
  1. Bu var daha hızlı , os.systemve subprocess.callbir şey bu basit için gereksizdir yeni süreçler oluşturun. Aslında, os.systemve subprocess.callbirlikte shellkabuk olmak ilki ve (built-in gibi bir kabuk değilse yayınladığınız o komut olmanın ikincisi: argüman genellikle en az iki yeni süreçler yaratmak test).

  2. Bazı komutlar ayrı bir işlemde işe yaramaz . Örneğin, çalıştırırsanız os.spawn("cd dir/"), alt işlemin geçerli çalışma dizinini değiştirir, ancak Python işleminin değiştirmez. Bunun için kullanmalısın os.chdir.

  3. Kabuk tarafından yorumlanan özel karakterler için endişelenmenize gerek yok . os.chmod(path, mode)dosya adı ne olursa olsun çalışır, dosya adı böyle bir os.spawn("chmod 777 " + path)şeyse korkunç bir şekilde başarısız olur ; rm -rf ~. ( Argüman subprocess.callolmadan kullanırsanız bu shellsorunu çözebileceğinizi unutmayın.)

  4. Kısa çizgiyle başlayan dosya adları hakkında endişelenmenize gerek yok . os.chmod("--quiet", mode)adlı dosyanın izinlerini değiştirir --quiet, ancak bağımsız değişken olarak yorumlanırsa os.spawn("chmod 777 --quiet")başarısız olur --quiet. Bu bile için geçerlidir subprocess.call(["chmod", "777", "--quiet"]).

  5. Python'un standart kütüphanesinin sizin için bununla ilgilenmesi gerektiğinden, platformlar arası ve çapraz kabuk endişeleriniz daha azdır . Sisteminizde chmodkomut var mı? Yüklü mü? Desteklemesini beklediğiniz parametreleri destekliyor mu? osBu mümkün değil o zaman modül mümkün olduğunca çapraz platform ve belgeler gibi olmaya çalışacağım.

  6. Çalıştırdığınız komutun önem verdiğiniz çıktısı varsa, köşe durumlarını (boşluklar, sekmeler ve yeni satırlar içeren dosya adları) unutabileceğiniz gibi, göründüğünden daha zor olan ayrıştırmanız gerekir. taşınabilirlik umurumda değil.


38
"Çapraz platform" noktasına eklemek için, bir dizin listeleme linux üzerinde "ls", windows üzerinde "dir" dir. Bir dizinin içeriğini almak çok yaygın bir alt düzey görevdir.
Cort Ammon

1
@CortAmmon: "Düşük Seviye" göreceli lsveya dirbelirli geliştiricilere göre oldukça yüksek seviyelerde, tıpkı bashya cmdda kshya da tercih ettiğiniz kabuk.
Sebastian Mach

1
@phresnel: Bunu hiç böyle düşünmemiştim. Bana göre, "İşletim Sisteminizin çekirdek API'sine doğrudan çağrı" çok düşük düzeydeydi. Bu konuda benden kaçan farklı bir bakış açısı olduğunu varsayıyorum çünkü (doğal olarak) kendi önyargılarımla ona yaklaşıyorum.
Cort Ammon

5
@CortAmmon: doğru ve lsişletim sisteminizin çekirdek API'sine doğrudan bir çağrı olmadığı için bundan daha yüksek. (Küçük) bir uygulamadır.
Steve Jessop

1
@SteveJessop. Ben "bir dizinin içeriğini almak" düşük seviye aradım. Ben düşünmüyorum lsya dirama opendir()/readdir()(linux api) ya da FindFirstFile()/FindNextFile()(windows api) ya da File.listFiles(java API) ya da Directory.GetFiles()(C #). Bunların tümü, işletim sistemine doğrudan bir çağrı ile yakından ilişkilidir. Bazıları bir sayıyı bir kayıt defterine itmek ve int 13hçekirdek modunu tetiklemek kadar basit olabilir .
Cort Ammon

133

Daha emniyetli. Burada size bir fikir vermek için örnek bir senaryo

import os
file = raw_input("Please enter a file: ")
os.system("chmod 777 " + file)

Kullanıcının girişi test; rm -rf ~bu olsaydı, giriş dizini silinirdi.

Bu nedenle yerleşik işlevi kullanmak daha güvenlidir.

Bu nedenle neden sistem yerine alt süreci de kullanmalısınız.


26
Ya da bakmanın başka bir yolu, doğru olanı daha kolay, Python programları yazmak veya kabuk komut dosyaları yazan Python programları yazmak mı? :-)
Steve Jessop

3
@SteveJessop, bir meslektaşım, 20 (!) Kat daha hızlı bronz kabuk betiği yazmasına yardım ettiğim küçük bir Python betiğine hayran kaldı. Çıktı yeniden yönlendirmesinin seksi görünebileceğini açıkladım - ancak her yinelemede dosyayı açmayı ve kapatmayı gerektirir. Ama bazı şeyler zor yoldan yapmayı sever - :)
volkan

1
@SteveJessop, bu hileli bir soru - çalışma zamanına kadar bilemezsiniz! :)

60

Komutu oskullanırken Python'un modüldeki daha spesifik yöntemlerini os.systemveya subprocessmodülü çalıştırırken tercih etmenin dört güçlü durumu vardır :

  • Fazlalık - başka bir işlemin ortaya çıkması gereksizdir ve zaman ve kaynak israfına neden olur.
  • Taşınabilirlik - osModüldeki yöntemlerin çoğu birden fazla platformda bulunurken, birçok kabuk komutu işletim sistemine özgüdür.
  • Sonuçları Anlamak - Eğer çıkışından sonuçlarını ayrıştırmak ve anlaşılması keyfi komutları güçlerini çalıştırmak için bir süreç Yumurtlama eğer ve niçin bir komut şey yanlış yaptı.
  • Güvenlik - Bir süreç potansiyel olarak verdiği komutu yürütebilir. Bu zayıf bir tasarımdır ve osmodüldeki belirli yöntemler kullanılarak önlenebilir .

Fazlalık (bkz. Artık kod ):

Aslında, nihai sistem çağrılarına giderken gereksiz bir "orta adamı" yürütüyorsunuz ( chmodörneğin). Bu orta insan yeni bir süreç ya da alt kabuktur.

Gönderen os.system:

Komutu (dize) alt kabukta yürütün ...

Ve subprocesssadece yeni süreçleri ortaya çıkarmak için bir modül.

Bu süreçleri yumurtlamadan ihtiyacınız olanı yapabilirsiniz.

Taşınabilirlik (bkz. Kaynak kodu taşınabilirliği ):

osModülün amacı jenerik işletim sistemi hizmetleri sağlamak için olduğunu ve birlikte açıklama başlar bulunuyor:

Bu modül, işletim sistemine bağlı işlevselliği kullanmak için taşınabilir bir yol sağlar.

Sen kullanabilirsiniz os.listdirpencere ve Unix hem. Bu işlevselliği os.system/ subprocessiçin kullanmaya çalışmak sizi iki çağrıyı sürdürmeye ( ls/ için dir) zorlar ve hangi işletim sistemini kullandığınızı kontrol eder. Bu taşınabilir olarak değil ve olacak (bkz sonra daha da hayal kırıklığı neden Handling Çıktı ).

Komutun sonuçlarını anlama:

Bir dizindeki dosyaları listelemek istediğinizi varsayalım.

Eğer kullanıyorsanız os.system("ls")/ subprocess.call(['ls']), sadece temelde dosya adları ile büyük bir dizidir sürecin çıkış geri alabilirsiniz.

Adında boşluk bulunan bir dosyayı iki dosyadan nasıl anlatabilirsiniz?

Dosyaları listeleme izniniz yoksa ne olur?

Verileri python nesnelerine nasıl eşlemelisiniz?

Bunlar sadece kafamın üstünde ve bu sorunların çözümleri varken - neden sizin için çözülmüş bir sorunu tekrar çözelim?

Bu aşağıdaki örneğidir Do not tekrar tarafından (Genellikle olarak "kuru" refere) prensibi değil zaten var ve sizin için serbestçe kullanılabilir bir uygulama tekrarlayarak.

Emniyet:

os.systemve subprocessgüçlü. Bu güce ihtiyacınız olduğunda iyidir, ancak ihtiyacınız olmadığında tehlikelidir. Kullandığınızda os.listdir, dosyaları listelemek veya bir hata oluşturmaktan başka bir şey yapamayacağını biliyorsunuz . Aynı davranışı kullandığınızda os.systemveya subprocesselde ettiğinizde, potansiyel olarak yapmak istemediğiniz bir şeyi yapabilirsiniz.

Enjeksiyon Güvenliği ( kabuk enjeksiyon örneklerine bakın ) :

Kullanıcının girdisini yeni bir komut olarak kullanırsanız, ona temel olarak bir kabuk vermiş olursunuz. Bu, kullanıcı için DB'de bir kabuk sağlayan SQL enjeksiyonuna çok benzer.

Bir örnek, formun bir komutudur:

# ... read some user input
os.system(user_input + " some continutation")

Bu olayı oluşturmak için herhangi bir rasgele kodu çalıştırmak için kolayca kullanılabilir NASTY COMMAND;#:

os.system("NASTY COMMAND; # some continuation")

Sisteminizi riske atabilecek böyle birçok komut vardır.


3
2. temel nedeni olduğunu söyleyebilirim.
jaredad7

23

Basit bir nedenle - kabuk işlevini çağırdığınızda, komutunuz var olduktan sonra yok edilen bir alt kabuk oluşturur, bu nedenle bir kabuktaki dizini değiştirirseniz - Python'daki ortamınızı etkilemez.

Ayrıca, alt kabuk oluşturmak zaman alıcıdır, bu nedenle OS komutlarını doğrudan kullanmak performansınızı etkiler

DÜZENLE

Çalışan bazı zamanlama testleri vardı:

In [379]: %timeit os.chmod('Documents/recipes.txt', 0755)
10000 loops, best of 3: 215 us per loop

In [380]: %timeit os.system('chmod 0755 Documents/recipes.txt')
100 loops, best of 3: 2.47 ms per loop

In [382]: %timeit call(['chmod', '0755', 'Documents/recipes.txt'])
100 loops, best of 3: 2.93 ms per loop

Dahili işlev 10 kat daha hızlı çalışır

EDIT2

Harici yürütülebilir dosyayı çağırmanın Python paketlerinden daha iyi sonuçlar verebileceği durumlar olabilir - bir meslektaşım tarafından gönderilen bir postayı , alt işlem yoluyla çağrılan gzip performansının, kullandığı bir Python paketinin performansından çok daha yüksek olduğunu hatırladım . Ancak standart işletim sistemi komutlarını taklit eden standart işletim sistemi paketleri hakkında konuşurken kesinlikle değil


Herhangi bir tesadüf iPython ile yapılır? %Normal yorumlayıcıyı kullanarak başlayan özel işlevleri kullanabileceğinizi düşünmüyordum .
iProgram

@aPyDeveloper, evet, iPython'du - Ubuntu'da. "Büyülü" % timeit bir nimettir - ancak çoğunlukla dize biçimlendirmesi olan - işleyemediği bazı durumlar olsa da
yanardağ

1
Ya da bir python betiği yapabilir ve sonra time <path to script> terminali yazabilirsiniz ve size gerçek, kullanıcı ve işlem süresini söyleyecektir. İPython'unuz yoksa ve Unix komut satırına erişiminiz varsa.
iProgram

1
@aPyDeveloper, çok çalışmak için bir neden görmüyorum - makinemde iPython varken
volkan

Doğru! İPython'unuz yoksa dedim. :)
iProgram

16

Kabuk çağrısı işletim sistemine özgüdür, oysa Python os modülü işlevleri çoğu durumda değildir. Ve bir alt işlemin ortaya çıkmasını önler.


1
Python modülü işlevleri, yeni bir alt kabuk oluşturmak için yeni alt süreçleri de oluşturur.
Koderok

7
@Koderok saçmalık, modül fonksiyonları süreç içinde çağrılır
dwurf

3
@Koderok: os modülü, shell komutunun kullandığı temel sistem çağrılarını kullanır, shell komutlarını kullanmaz. Bu, os sistem çağrısının kabuk komutlarından genellikle daha güvenli ve daha hızlı olduğu anlamına gelir (dize ayrıştırma yok, boo fork, exec yok, sadece çekirdek çağrısı). Çoğu durumda, kabuk çağrısı ve sistem çağrısı genellikle benzer veya aynı ada sahiptir, ancak ayrı olarak belgelenmiştir; kabuk çağrısı man bölümü 1'de (varsayılan man bölümü), eşdeğer olarak adlandırılmış sistem çağrısı man bölümü 2'de (örn. man 2 chmod).
Lie Ryan

1
@ dwurf, LieRyan: Benim hatam! Yanlış bir fikrim vardı, öyle görünüyor. Teşekkürler!
Koderok

11

Çok daha verimli. "Kabuk" sadece bir çok sistem çağrısı içeren başka bir OS ikili dosyasıdır. Neden sadece bu tek sistem çağrısı için tüm kabuk sürecini yaratma yükü?

os.systemKabuk yerleşik olmayan bir şey için kullandığınızda durum daha da kötüdür . Bir kabuk işlemini başlatırsınız, bu durumda (iki işlem uzakta) sistem çağrısı yapan bir yürütülebilir dosya başlatır. En azından subprocessbir kabuk aracı sürecine olan ihtiyacı ortadan kaldırırdı.

Bu Python'a özgü değil. systemdaynı sebeple Linux başlangıç ​​zamanlarında böyle bir gelişmedir: Binlerce mermi yumurtlamak yerine gerekli sistemi kendisi çağırır.

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.