web-dev-qa-db-ja.com

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

subprocessモジュールを使用してさまざまなプロセスを呼び出しています。しかし、質問があります。

次のコードでは:

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

そして

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

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

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

206
user225312

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

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

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

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

要約すると、Shell=Falseを使用してください。

147
Heath Hunnicutt
>>> 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値に設定すると、サブプロセスは中間シェルプロセスを生成し、コマンドを実行するように指示します。つまり、中間シェルを使用すると、コマンド文字列内の変数、グロブパターン、およびその他の特殊なシェル機能が、コマンドの実行前に処理されます。ここで、この例では、$ HOMEはechoコマンドの前に処理されました。実際、これはコマンドls -lが単純なコマンドであると考えられているシェル拡張のコマンドの場合です。

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

81
Mina Gabriel

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

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

要するに:Shell=Trueは絶対に避けてください。

35
lunaryorn

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()

32
Richeek

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

ワイルドカードの展開、変数の補間、およびリダイレクトはすべて、ネイティブのPythonコンストラクトに置き換えるのが簡単です。部分またはすべてをPython(特殊な外部ツール、おそらくクローズドソース?)で合理的に書き換えることができない複雑なシェルパイプラインは、おそらくシェルの使用を検討できる1つの状況です。あなたはまだそれについて気分が悪いはずです。

些細なケースでは、単に置き換える

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

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

最初の引数がexecvp()に渡す文字列のリストであることに注意してください。また、文字列のクォートとバックスラッシュエスケープシェルメタ文字が一般に必要ない(または有用、または正しい)ことに注意してください。

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

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

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

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

15
tripleee