サブプロセスでの「shell = True」の実際の意味


260

subprocessモジュールで別のプロセスを呼び出しています。しかし、質問があります。

次のコードでは:

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

そして

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

どちらも機能します。ドキュメントを読んだ後、私はそれがshell=Trueシェルを介してコードを実行することを意味することを知った。つまり、不在の場合、プロセスは直接開始されます。

だから私は私の場合に何を好むべきですか-プロセスを実行してその出力を取得する必要があります。シェルの内部または外部から呼び出すことの利点は何ですか。


21
最初のコマンドが正しくありません:-lに渡されます/bin/sh(シェル)の代わりにlsプログラムのUnix上の場合shell=Trueshell=Trueほとんどの場合、リストの代わりに文字列引数を使用する必要があります。
jfs 2014

1
re「プロセスが直接開始される」:Wut?
allyourcode

9
「両方の仕事」という文。これらの2つの呼び出しについては正しくなく、誤解を招きます。呼び出しの動作は異なります。からshell=TrueFalse、またはその逆に切り替えるだけではエラーになります。ドキュメントから:「shell = Trueを使用したPOSIXでは、(...)argsがシーケンスの場合、最初の項目はコマンド文字列を指定し、追加の項目はシェル自体への追加の引数として扱われます。」Windowsでは自動変換が行われますが、これは望ましくない場合があります。
mbdevpl 2016年

回答:


183

シェル経由で呼び出さないことの利点は、「ミステリープログラム」を呼び出さないことです。POSIXでは、環境変数SHELLが「シェル」として呼び出されるバイナリを制御します。Windowsでは、子孫のボーンシェルはなく、cmd.exeのみです。

したがって、シェルを呼び出すと、ユーザーが選択したプログラムが呼び出され、プラットフォームに依存します。一般的に言って、シェルを介した呼び出しは避けてください。

シェルを介して呼び出すと、シェルの通常のメカニズムに従って環境変数とファイルグロブを拡張できます。POSIXシステムでは、シェルはファイルグロブをファイルのリストに展開します。Windowsでは、ファイルグロブ(例えば、「*。*」)で、とにかく、シェルによって展開されていません(ただし、コマンドラインで環境変数がされている cmd.exeので拡大)。

環境変数の拡張とファイルグロブが必要だと思われる場合ILSは、シェル経由でサブプログラムの呼び出しを実行したネットワークサービスに対する1992-ishの攻撃を調査してください。例には、を含むさまざまなsendmailバックドアが含まれILSます。

要約すると、を使用しますshell=False


2
答えてくれてありがとう。私は実際にはエクスプロイトを心配すべき段階ではありませんが、あなたが何をしているのか理解しています。
user225312

55
最初から不注意だったら、心配することは後で追いつくのに役立ちません。;)
Heath Hunnicutt、

サブプロセスの最大メモリを制限したい場合はどうなりますか?stackoverflow.com/questions/3172470/...
Pramodさん

8
についての文$SHELLは正しくありません。subprocess.htmlを引用するには:「Unixのshell=True場合、シェルのデフォルトは/bin/shです。」(ではない$SHELL
marcin 2016

1
@ user2428107:はい、Perlでバックティック呼び出しを使用すると、シェル呼び出しを使用して同じ問題が発生します。openプログラムを呼び出して出力をキャプチャする安全な方法が必要な場合は、3 + argを使用します。
ShadowRanger 2016年

137
>>> 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

shell引数をtrue値に設定すると、サブプロセスが中間シェルプロセスを生成し、コマンドを実行するように指示します。つまり、中間シェルを使用すると、コマンド文字列内の変数、グロブパターン、その他の特別なシェル機能が、コマンドが実行される前に処理されます。ここの例では、echoコマンドの前に$ HOMEが処理されています。実際、これはコマンドls -lが単純なコマンドと見なされている場合のシェル展開を伴うコマンドの場合です。

ソース:サブプロセスモジュール


16
これが選択された回答ではない理由がわかりません。はるかに実際に質問に一致する問題
Rodrigo Lopez Guerra

1
同意する。これは、shell = Trueの意味を理解するための良い例です。
user389955 2017

2
シェル引数をtrue値に設定すると、サブプロセスが中間シェルプロセスを生成し、コマンドを実行する ように指示します。なぜこの答えは受け入れられないのですか??? どうして?
pouya

問題は、呼び出す最初の引数が文字列ではなくリストであることですが、シェルがFalseの場合はエラーが発生します。コマンドをリストに変更すると、これが機能します
リンカーンランドールマクファーランド

申し訳ありませんが、私の前のコメントは私が完了する前に行きました。明確にするために、シェル= Trueでサブプロセスを使用することがよくあり、コマンドは文字列です(例: 'ls -l')(このエラーを回避することを期待しています)が、サブプロセスはリスト(および文字列を1つの要素のリストとして)を取ります。シェルを呼び出さずに実行するには(およびそのセキュリティ問題)、リストsubprocess.call(['ls'、 '-l'])を使用します
リンカーンランドールマクファーランド

42

Shell = Trueで問題が発生する可能性がある例を次に示します

>>> 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...

ここのドキュメントを確認してください:subprocess.call()


6
リンクは非常に便利です。リンクが述べたように:信頼できないソースからの無害化された入力を組み込んだシェルコマンドを実行すると、プログラムがシェルインジェクションに対して脆弱になります。これは、任意のコマンドの実行を引き起こす可能性がある深刻なセキュリティ上の欠陥です。このため、コマンド文字列が外部入力から構成される場合は、shell = Trueを使用しないことを強くお勧めします。
jtuki

39

シェルを介してプログラムを実行するとは、プログラムに渡されるすべてのユーザー入力が、呼び出されたシェルの構文と意味規則に従って解釈されることを意味します。ユーザーがこれらのルールに従う必要があるため、せいぜい、これはユーザーに不便をもたらすだけです。たとえば、引用符や空白などの特殊なシェル文字を含むパスはエスケープする必要があります。最悪の場合、ユーザーが任意のプログラムを実行できるため、セキュリティリークが発生します。

shell=True単語分割やパラメータ拡張などの特定のシェル機能を利用すると便利な場合があります。ただし、そのような機能が必要な場合は、他のモジュールを使用してください(たとえばos.path.expandvars()、パラメーターの拡張やshlex単語分割など)。これはより多くの作業を意味しますが、他の問題を回避します。

つまり、絶対に避けてくださいshell=True


16

ここでの他の回答は、subprocessドキュメントにも記載されているセキュリティ上の警告を適切に説明しています。しかし、それに加えて、実行したいプログラムを開始するためにシェルを開始するオーバーヘッドは、多くの場合不要であり、実際にシェルの機能をまったく使用しない状況では間違いなくばかげています。さらに、シェルやシェルが提供するサービスに精通していない場合は特に、追加の隠された複雑さがあなたを怖がらせるはずです。

シェルとの相互作用が重要な場合は、Pythonスクリプトとシェルスクリプトの両方を理解するために、Pythonスクリプトの読者とメンテナー(将来の自分ではないかもしれません)が必要になります。Pythonのモットー「明示的なものは暗黙的なものより優れている」を覚えておいてくださいPythonコードが同等の(多くの場合非常に簡潔な)シェルスクリプトよりも多少複雑になる場合でも、シェルを削除して機能をネイティブのPython構成体に置き換える方がよいでしょう。外部プロセスで行われる作業を最小限に抑え、独自のコード内で可能な限り制御を維持することは、可視性が向上し、副作用のリスク(望まれるまたは望まれない)が軽減されるため、多くの場合良い考えです。

ワイルドカード拡張、変数補間、およびリダイレクトはすべて、ネイティブPython構成で置き換えるのが簡単です。パーツまたはすべてをPythonで合理的に書き換えることができない複雑なシェルパイプラインは、おそらくシェルの使用を検討できる状況の1つです。それでも、パフォーマンスとセキュリティへの影響を確実に理解する必要があります。

些細なケースでは、を避けるためにshell=True、単に

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

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

最初の引数がに渡す文字列のリストであることexecvp()、および文字列を引用符で囲んでシェルメタ文字をバックスラッシュでエスケープする必要がない(または有用である、または正しい)ことに注意してください。シェル変数を引用符で囲むタイミングも参照してください

余談ですPopenが、subprocessパッケージ内のより単純なラッパーの1つが希望どおりに機能するかどうかを避けたいことがよくあります。最近十分なPythonを使用してsubprocess.runいる場合は、おそらくを使用する必要があります。

  • ではcheck=True、実行したコマンドが失敗した場合、それは失敗します。
  • これを使用stdout=subprocess.PIPEすると、コマンドの出力がキャプチャされます。
  • ややあいまいですが、universal_newlines=True出力は適切なUnicode文字列にデコードされます(bytesPython 3では、それ以外の場合はシステムエンコーディングに含まれます)。

そうでない場合、多くのタスクでcheck_output、コマンドが成功したことを確認しながら、またはcheck_call収集する出力がない場合に、コマンドから出力を取得する必要があります。

David Kornからの引用で締めくくります:「ポータブルシェルスクリプトよりもポータブルシェルを書く方が簡単です。」でも、subprocess.run('echo "$HOME"', shell=True)Windowsに移植性がありません。


引用はラリーウォールからのものだと思っていましたが、Googleはそれ以外のことを教えてくれました。
tripleee 2016年

それはハイトークですが、置き換えに関する技術的な提案はありません。ここでは、OS-Xで、 'open'を介して起動したMacアプリのPIDを取得しようとしています:process = subprocess.Popen( '/ usr / bin / pgrep- n '+ app_name、shell = False、stdout = subprocess.PIPE、stderr = subprocess.PIPE)app_pid、err = process.communicate()---しかし、shell = Trueを使用しない限り機能しません。それで?
Motti Shneor 16

回避方法についてshell=Trueはたくさんの質問があり、その多くは優れた答えを持っています。あなたはたまたまその理由を説明したものを選びました。
Tripleee 2016

@MottiShneorフィードバックありがとうございます。簡単な例を追加
tripleee

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.