Os.system () çağrılarından nasıl kaçılır?


124

Os.system () kullanılırken genellikle dosya adlarından ve komutlara parametre olarak geçirilen diğer argümanlardan kaçınmak gerekir. Bunu nasıl yapabilirim? Tercihen birden çok işletim sistemi / kabuk üzerinde ama özellikle bash için çalışacak bir şey.

Şu anda aşağıdakileri yapıyorum, ancak bunun için bir kitaplık işlevi veya en azından daha zarif / sağlam / verimli bir seçenek olması gerektiğinden eminim:

def sh_escape(s):
   return s.replace("(","\\(").replace(")","\\)").replace(" ","\\ ")

os.system("cat %s | grep something | sort > %s" 
          % (sh_escape(in_filename), 
             sh_escape(out_filename)))

Düzenleme: Alıntı kullanmanın basit cevabını kabul ettim, neden bunu düşünmediğimi bilmiyorum; Sanırım Windows'dan geldiğim için 've "biraz farklı davranıyorum.

Güvenlikle ilgili olarak endişeyi anlıyorum, ancak bu durumda os.system () 'in sağladığı hızlı ve kolay bir çözümle ilgileniyorum ve dizelerin kaynağı ya kullanıcı tarafından oluşturulmamış ya da en azından bir güvenilen kullanıcı (ben).


1
Güvenlik sorununa dikkat edin! Örneğin, out_filename foo.txt ise; rm -rf / Kötü niyetli kullanıcı, doğrudan kabuk tarafından yorumlanan daha fazla komut ekleyebilir.
Steve Gury

6
Bu, alt işlemin bir seçenek olmadığı durumlarda, os.system olmadan da yararlıdır; örneğin, kabuk komut dosyaları oluşturmak.

İdeal bir sh_escapeişlev, ;ve boşluklarından kaçabilir ve basitçe benzeri bir şey olarak adlandırılan bir dosya oluşturarak güvenlik sorununu ortadan kaldırabilir foo.txt\;\ rm\ -rf\ /.
Tom

Neredeyse tüm durumlarda, os.system'i değil, alt süreci kullanmalısınız. Os.system'i aramak sadece bir enjeksiyon saldırısı istemektir.
allyourcode

Yanıtlar:


85

Kullandığım şey bu:

def shellquote(s):
    return "'" + s.replace("'", "'\\''") + "'"

Kabuk, alıntılanan bir dosya adını her zaman kabul eder ve söz konusu programa aktarmadan önce çevredeki tırnak işaretlerini kaldırır. Özellikle bu, boşluklar veya diğer kötü kabuk meta karakterleri içeren dosya adlarıyla ilgili sorunları önler.

Güncelleme : Python 3.3 veya sonraki bir sürümünü kullanıyorsanız, kendinizinkini döndürmek yerine shlex.quote kullanın.


7
@pixelbeat: İşte tam da bu yüzden tek alıntılarını kapatıyor, kaçmış gerçek tek bir alıntı ekliyor ve ardından tek alıntılarını yeniden açıyor.
lhunath

4
Bu, shellquote işlevinin sorumluluğu olmasa da, bu işlevin dönüş değerinden hemen önce tırnaksız bir ters eğik çizgi görünürse, bunun yine de başarısız olacağını not etmek ilginç olabilir. Moral: Bunu güvenli olarak güvenebileceğiniz bir kodda kullandığınızdan emin olun - (kodlanmış komutların bir parçası gibi) - onu diğer alıntılanmamış kullanıcı girdilerine eklemeyin.
lhunath

10
Kabuk özelliklerine kesinlikle ihtiyacınız olmadıkça, bunun yerine Jamie'nin önerisini kullanmanız gerektiğini unutmayın.
lhunath

6
Buna benzer bir şey artık resmi olarak shlex.quote olarak mevcuttur .
Janus Troelsen

3
Bu yanıtta sağlanan işlev kabuk alıntı yapmakta shlexveya yerine daha iyi bir iş çıkarır pipes. Bu python modülleri hatalı olarak, alıntılanması gereken tek şeyin özel karakter olduğunu varsayar, bu da, bu davranış beklenmediğinde kabuk anahtar kelimelerinin (gibi time, caseveya while) ayrıştırılacağı anlamına gelir . Bu nedenle, bu cevapta tek alıntı rutini kullanmanızı tavsiye ederim, çünkü "zeki" olmaya çalışmadığı için bu aptalca uç durumlara sahip değildir.
user3035772

157

shlex.quote() python 3'ten beri istediğinizi yapar.

( pipes.quoteHem python 2'yi hem de python 3'ü desteklemek için kullanın )


Ayrıca var commands.mkarg. Aynı zamanda, arzu edilen veya olmayabilen (alıntıların dışında) bir ön alan ekler. Uygulamalarının birbirinden oldukça farklı olması ve aynı zamanda Greg Hewgill'in cevabından çok daha karmaşık olması ilginçtir.
Laurence Gonsalves

3
Nedense, pipes.quotetarafından belirtilmeyen borular için standart kütüphane dokümantasyon modülü
Günlük

1
Her ikisi de belgelenmemiş; command.mkarg3.x sürümünde kullanımdan kaldırıldı ve kaldırıldı, borular.quote kaldı.
Beni Cherniavsky-Paskin

9
Düzeltme: shlex.quote()3.3'te olduğu gibi resmi olarak belgelenmiştir , pipes.quote()uyumluluk için saklanmıştır. [ bugs.python.org/issue9723]
Beni Cherniavsky-Paskin

7
borular Windows'ta ÇALIŞMAZ - çift tırnaklardan oluşan tek tırnaklar ekler.
Nux

58

Belki kullanmak için belirli bir nedeniniz vardır os.system(). Ama değilse, muhtemelen subprocessmodülü kullanıyor olmalısın . Boruları doğrudan belirleyebilir ve kabuğu kullanmaktan kaçınabilirsiniz.

Aşağıdakiler PEP324'ten alınmıştır :

Replacing shell pipe line
-------------------------

output=`dmesg | grep hda`
==>
p1 = Popen(["dmesg"], stdout=PIPE)
p2 = Popen(["grep", "hda"], stdin=p1.stdout, stdout=PIPE)
output = p2.communicate()[0]

6
subprocess(özellikle check_callvb. ile) genellikle önemli ölçüde üstündür, ancak kabuk kaçışının hala yararlı olduğu birkaç durum vardır. Karşılaştığım en önemli şey, ssh uzaktan komutlarını çağırmak zorunda olduğum zamandır.
Craig Ringer

@CraigRinger, evet, ssh remoting beni buraya getirdi. : Keşke ssh'nin burada yardımcı olacak bir şeyi olsaydı.
Jürgen A. Erhard

@ JürgenA.Erhard Bir --execvp-remote seçeneğinin olmaması (veya varsayılan olarak bu şekilde çalışması) garip görünüyor. Her şeyi kabuk aracılığıyla yapmak beceriksiz ve riskli görünüyor. OTOH, ssh tuhaf tuhaflıklar ile doludur, genellikle dar bir "güvenlik" bakış açısıyla yapılan işler, insanların çok daha güvensiz geçici çözümler bulmasına neden olur.
Craig Ringer

10

Belki subprocess.list2cmdlinedaha iyi bir atıştır?


Bu oldukça iyi görünüyor. Belgelenmemiş ilginç ... ( en azından docs.python.org/library/subprocess.html'de )
Tom

4
Düzgün bir şekilde kaçmıyor \: subprocess.list2cmdline(["'",'',"\\",'"'])verir' "" \ \"
Tino

Mermi genişletme sembollerinden kaçmıyor
grep

Subprocess.list2cmdline () yalnızca Windows için mi tasarlandı?
JS.

@JS Evet, list2cmdlineWindows cmd.exe sözdizimine uygundur ( Python kaynak kodundaki docstring işlevine bakın ). shlex.quoteUnix bourne kabuk sözdizimine uygundur, ancak Unix'in argümanları doğrudan iletmek için iyi bir desteği olduğundan genellikle gerekli değildir. Windows hemen hemen tüm argümanlarınızla tek bir dizge geçmenizi gerektirir (bu nedenle uygun şekilde kaçma ihtiyacı).
eestrada

7

Pipetler.quote'un Python 2.5 ve Python 3.1'de gerçekten bozuk olduğunu ve kullanımının güvenli olmadığını unutmayın - Sıfır uzunluklu argümanları işlemez.

>>> from pipes import quote
>>> args = ['arg1', '', 'arg3']
>>> print 'mycommand %s' % (' '.join(quote(arg) for arg in args))
mycommand arg1  arg3

Bkz Python sorunu 7476 ; Python 2.6 ve 3.2 ve daha yeni sürümlerde düzeltildi.


4
Python'un hangi sürümünü kullanıyorsunuz? Sürüm 2.6 doğru çıktıyı üretiyor gibi görünüyor: mycommand arg1 '' arg3 (Bunlar bir arada iki tek tırnaktır, ancak Stack Overflow'daki yazı tipi bunu söylemeyi zorlaştırır!)
Brandon Rhodes

4

Farkına varmak : Bu Python 2.7.x için bir cevaptır.

Kaynağa göre , pipes.quote()" Bir dizeyi / bin / sh için tek bir bağımsız değişken olarak güvenilir şekilde alıntı yapmanın" bir yoludur . ( 2.7 sürümünden beri kullanımdan kaldırılmış ve nihayet Python 3.3'te genel olarakshlex.quote() işlev .)

Öte yandan , subprocess.list2cmdline()" MS C çalışma zamanıyla aynı kuralları kullanarak bir dizi bağımsız değişkeni komut satırı dizesine çevirmenin " bir yoludur .

Komut satırları için dizelerden alıntı yapmanın platformdan bağımsız yoluyuz.

import sys
mswindows = (sys.platform == "win32")

if mswindows:
    from subprocess import list2cmdline
    quote_args = list2cmdline
else:
    # POSIX
    from pipes import quote

    def quote_args(seq):
        return ' '.join(quote(arg) for arg in seq)

Kullanımı:

# Quote a single argument
print quote_args(['my argument'])

# Quote multiple arguments
my_args = ['This', 'is', 'my arguments']
print quote_args(my_args)

3

Os.system'in kullanıcı için yapılandırılan komut kabuğunu çağırdığına inanıyorum, bu yüzden bunu platformdan bağımsız bir şekilde yapabileceğinizi düşünmüyorum. Komut kabuğum bash, emacs, ruby ​​ve hatta quake3'ten herhangi bir şey olabilir. Bu programlardan bazıları, onlara ilettiğiniz türden argümanları beklemiyor ve yapmış olsalar bile, kaçışlarını aynı şekilde yapacaklarının garantisi yok.


2
Çoğunlukla veya tamamen POSIX uyumlu bir kabuk beklemek mantıksız değildir (en azından her yerde ancak Windows ile ve o zaman ne tür bir "kabuğa" sahip olduğunuzu bilirsiniz). os.system $ SHELL kullanmıyor, en azından burada değil.

2

Kullandığım işlev:

def quote_argument(argument):
    return '"%s"' % (
        argument
        .replace('\\', '\\\\')
        .replace('"', '\\"')
        .replace('$', '\\$')
        .replace('`', '\\`')
    )

yani, argümanı her zaman çift tırnak içine alırım ve sonra çift tırnak içindeki özel karakterleri ters eğik çizgi ile alıntı yaparım.


'\\ "', '\\ $' ve '\`' kullanmanız gerektiğini unutmayın, aksi takdirde kaçış olmaz.
JanKanis

1
Ek olarak, bazı (garip) yerel ayarlarda çift tırnak kullanımıyla ilgili sorunlar vardır ; pipes.quote@JohnWiseman'ın belirttiği önerilen düzeltme kullanımları da bozuktur. Greg Hewgill'in cevabı, bu yüzden kullanılacak cevaptır. (Aynı zamanda mermilerin normal vakalar için dahili olarak kullandığı da.)
mirabilos

-3

Sistem komutunu kullanırsanız, os.system () çağrısına nelerin girildiğini beyaz listeye almaya çalışırdım .. Örneğin ..

clean_user_input re.sub("[^a-zA-Z]", "", user_input)
os.system("ls %s" % (clean_user_input))

Alt işlem modülü daha iyi bir seçenektir ve mümkün olan her yerde os.system / subprocess gibi herhangi bir şeyi kullanmaktan kaçınmanızı tavsiye ederim.


-3

Gerçek cevap şudur: os.system()İlk etapta kullanmayın . subprocess.callBunun yerine, çıkış karaktersiz bağımsız değişkenleri kullanın .


6
Soru, alt işlemin başarısız olduğu bir örnek içerir. Alt işlem kullanabiliyorsanız, kesinlikle kullanmalısınız. Ama yapamıyorsanız ... alt süreç her şey için bir çözüm değildir . Oh, ve cevabınız soruya hiç cevap vermiyor.
Jürgen A. Erhard

@ JürgenA.Erhard, OP'nin örneği, kabuk boru kullanmak istediği için başarısız olmuyor mu? Her zaman alt işlemi kullanmalısınız çünkü o bir kabuk kullanmaz. Bu biraz beceriksiz bir örnek , ancak yerel alt işlemlerde borular yapabilirsiniz, bunu kolaylaştırmaya çalışan birkaç pypi paketi vardır. İhtiyacım olan son işlemeyi mümkün olduğunca python'da yapma eğilimindeyim, her zaman kendi StringIO tamponlarınızı oluşturabilir ve alt işlemlerle işleri tamamen kontrol edebilirsiniz.
ThorSummoner
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.