24

を使用subprocess.Popen(args, shell=True)して " " を実行するとgcc --version(例として)、Windows では次のようになります。

>>> from subprocess import Popen
>>> Popen(['gcc', '--version'], shell=True)
gcc (GCC) 3.4.5 (mingw-vista special r3) ...

そのため、期待どおりにバージョンをうまく印刷しています。しかし、Linux では次のようになります。

>>> from subprocess import Popen
>>> Popen(['gcc', '--version'], shell=True)
gcc: no input files

gcc が--versionオプションを受け取っていないためです。

このドキュメントでは、Windows で args がどうなるかを正確に指定していませんが、Unix では、「args がシーケンスの場合、最初の項目はコマンド文字列を指定し、追加の項目は追加のシェル引数として扱われます。 ." Popen(arglist)IMHO Windows の方法の方が優れています。これにより、呼び出しを呼び出しと同じように扱うことができるからですPopen(arglist, shell=True)

ここで Windows と Linux の違いはなぜですか?

4

3 に答える 3

15

実際に Windows では、cmd.exewhenを使用します -シェル引数のshell=True先頭に追加しますcmd.exe /c(実際にはCOMSPEC環境変数を検索しますが、存在しない場合はデフォルトになります)。cmd.exe(Windows 95/98 では、中間w9xpopenプログラムを使用して実際にコマンドを起動します)。

したがって、奇妙な実装は、実際にはUNIX次のことを行う実装です (各スペースで異なる引数が区切られています)。

/bin/sh -c gcc --version

正しい実装 (少なくとも Linux では) は次のようになります。

/bin/sh -c "gcc --version" gcc --version

これにより、引用符で囲まれたパラメーターからコマンド文字列が設定され、他のパラメーターが正常に渡されるためです。

shマニュアルページセクションから-c

Read commands from the command_string operand instead of from the standard input. Special parameter 0 will be set from the command_name operand and the positional parameters ($1, $2, etc.) set from the remaining argument operands.

このパッチはかなり単純なようです:

--- subprocess.py.orig  2009-04-19 04:43:42.000000000 +0200
+++ subprocess.py       2009-08-10 13:08:48.000000000 +0200
@@ -990,7 +990,7 @@
                 args = list(args)

             if shell:
-                args = ["/bin/sh", "-c"] + args
+                args = ["/bin/sh", "-c"] + [" ".join(args)] + args

             if executable is None:
                 executable = args[0]
于 2009-08-10T11:09:49.550 に答える
5

subprocess.py ソースから:

UNIX で shell=True を使用: args が文字列の場合、シェルを介して実行するコマンド文字列を指定します。args がシーケンスの場合、最初の項目はコマンド文字列を指定し、追加の項目は追加のシェル引数として扱われます。

Windows の場合: Popen クラスは CreateProcess() を使用して、文字列を操作する子プログラムを実行します。args がシーケンスの場合、list2cmdline メソッドを使用して文字列に変換されます。すべての MS Windows アプリケーションがコマンド ラインを同じように解釈するわけではないことに注意してください。list2cmdline は、MS C ランタイムと同じ規則を使用するアプリケーション用に設計されています。

それは理由には答えません。期待される動作が見られていることを明確にするだけです。

exec*その「理由」はおそらく、UNIX ライクなシステムでは、コマンド引数が文字列の配列として(一連の呼び出しを使用して) アプリケーションに実際に渡されるためです。つまり、呼び出しプロセスは、各コマンド ライン引数に何が入るかを決定します。一方、シェルを使用するように指示すると、呼び出しプロセスは実際には、実行するシェルに単一のコマンドライン引数を渡す機会しか得られません。実行するコマンドライン全体、実行可能ファイルの名前と引数を単一の文字列として。

しかし、Windows では、(上記のドキュメントによると) コマンド ライン全体が単一の文字列として子プロセスに渡されます。CreateProcess APIのドキュメントを見ると、すべてのコマンド ライン引数が連結されて大きな文字列になることが想定されていることがわかります (したがって、 が呼び出されますlist2cmdline)。

さらに、UNIX ライクなシステムでは実際に便利なことを実行できるシェルが存在するという事実あるため、違いのもう 1 つの理由は、Windows ではshell=True何もしないことであると思われます。見る。2 つのシステムを同じように動作させる唯一の方法は、shell=TrueWindows でコマンド ライン引数をすべて削除することです。

于 2009-08-10T04:55:52.643 に答える