web-dev-qa-db-ja.com

os.systemに対するサブプロセスの利点

私は最近、サブプロセスがos.systemよりもはるかに優れていると言って、スタックオーバーフローに関するいくつかの投稿に遭遇しましたが、正確な利点を見つけるのは困難です。

私が遭遇したもののいくつかの例: https://docs.python.org/3/library/os.html#os.system

「サブプロセスモジュールは、新しいプロセスを生成し、その結果を取得するためのより強力な機能を提供します。この関数を使用するよりも、そのモジュールを使用することをお勧めします。」

それがどのようにしてより強力であるかはわかりませんが、サブプロセスを使用する方が多くの点で簡単ですが、実際には何らかの方法でより強力ですか?

別の例は次のとおりです。

https://stackoverflow.com/a/89243/3339122

subprocesssystemの利点は、より柔軟であることです(stdout、stderr、「実際の」ステータスを取得できます)コード、エラー処理の改善など)。

この投稿には2600以上の投票があります。繰り返しになりますが、より優れたエラー処理または実際のステータスコードが何を意味するのかについての詳細は見つかりませんでした。

その投稿に対するトップコメントは次のとおりです。

Os.systemを使用する理由が一目でわかりません。サブプロセスはとても良いようです。

繰り返しになりますが、いくつかの点で少し簡単になることは理解していますが、たとえばその理由はほとんど理解できません。

subprocess.call("netsh interface set interface \"Wi-Fi\" enable", Shell=True)

より良いです

os.system("netsh interface set interface \"Wi-Fi\" enabled")

誰かがそれがとても良い理由を説明できますか?

12
Lain

まず第一に、あなたは仲介者を切り捨てています。 subprocess.callはデフォルトで、コマンドを検査するシェルの生成を回避し、要求されたプロセスを直接生成します。問題の効率性の側面に加えて、デフォルトのシェルの動作をあまり制御できないため、これは重要です。通常、エスケープに関しては効果がありません。

特に、通常これを行うことはありません。

subprocess.call("netsh interface set interface \"Wi-Fi\" enable")

以来

単一の文字列を渡す場合、ShellTrueでなければなりません(以下を参照)。そうでない場合、文字列は引数を指定せずに実行するプログラムを単に指定する必要があります。

代わりに、次のようにします。

subprocess.call(["netsh", "interface", "set", "interface", "Wi-Fi", "enable"])

ここで、すべての脱出する悪夢がなくなっていることに注意してください。 subprocessはエスケープを処理するか(OSが引数を単一の文字列として要求する場合(Windowsなど))、分離された引数を関連するシステムコールに直接渡します(UNIXではexecvp)。

これを、特にクロスプラットフォームの方法でエスケープを自分で処理する必要がある場合と比較してください(cmdはPOSIX shと同じようにエスケープしません)。 (私を信じてください、cmd /kを呼び出すときに、コマンドに対して100%安全なエスケープを提供するために、不道徳な混乱が何であるかを知りたくありません)。

また、真ん中にシェルなしでsubprocessを使用すると、正しいリターンコードが返されます。プロセスの起動に失敗した場合、Python例外が発生します。戻りコードを取得した場合、それは実際には起動されたプログラムの戻りコードです。os.systemでは、知る方法がありません。返された戻りコードが起動されたコマンド(シェルが起動に成功した場合のデフォルトの動作)からのものであるか、シェルからのエラー(起動に失敗した場合)の場合。


引数の分割/エスケープと戻りコードに加えて、起動したプロセスをより適切に制御できます。 subprocess.callsubprocess機能よりも最も基本的なユーティリティ関数)を使用しても、stdinstdoutおよびstderrをリダイレクトして、通信することができます起動されたプロセスで。 check_callも同様であり、失敗の終了コードを無視するリスクを回避します。 check_outputは、check_callの一般的な使用例をカバーし、+すべてのプログラム出力を文字列変数にキャプチャします。

callと友達(os.systemと同じようにブロックされている)を通過すると、より強力な機能が使用できるようになります。特に、Popenオブジェクトを使用すると、起動したオブジェクトを操作できます非同期に処理します。あなたはそれを開始し、おそらくリダイレ​​クトされたストリームを介してそれと対話し、他のものを実行しながら時々実行されているかどうかを確認し、それが完了するのを待って、それに信号を送信してそれを殺します-単なるもの以外のすべてのものos.systemが提供する同期「シェルを介してデフォルトのstdin/stdout/stderrでプロセスを開始し、完了するまで待機する」。


つまり、subprocessで要約すると、

  • 最も基本的なレベル(call&友達)でも、あなたは:
    • Python引数のリストを渡すことにより、問題の回避を回避します。
    • シェルがコマンドラインをいじるのを避けてください。
    • 例外があるか、起動したプロセスの真の終了コードがあります。プログラム/シェルの終了コードに関する混乱はありません。
    • stdoutをキャプチャし、一般に標準ストリームをリダイレクトする可能性があります。
  • Popen:を使用する場合
    • 同期インターフェースに制限されていませんが、サブプロセスの実行中に他のことを実際に行うことができます。
    • サブプロセスを制御できます(実行中かどうかを確認し、通信し、強制終了します)。

subprocessos.systemが実行できるよりも多くの方法を実行し、さらに安全で柔軟な方法(必要な場合)で-代わりにsystemを使用する理由はありません。

16
Matteo Italia

多くの理由がありますが、主な理由はdocstringで直接言及されています。

>>> os.system.__doc__
'Execute the command in a subshell.'

サブプロセスが必要なほとんどすべての場合、それはサブシェルを生成することは望ましくないです。これは不要で無駄であり、複雑さがさらに増し、いくつかの新しい脆弱性と障害モードが発生します。 subprocessモジュールを使用すると、仲介者が排除されます。

6
wim