为什么带有 shell=True 的 subprocess.Popen() 在 Linux 和 Windows 上的工作方式不同?
问题描述
当使用 subprocess.Popen(args, shell=True)
运行gcc --version
"(仅作为示例),在 Windows 上我们得到:
When using subprocess.Popen(args, shell=True)
to run "gcc --version
" (just as an example), on Windows we get this:
>>> from subprocess import Popen
>>> Popen(['gcc', '--version'], shell=True)
gcc (GCC) 3.4.5 (mingw-vista special r3) ...
所以它可以很好地打印出我所期望的版本.但在 Linux 上,我们得到了这个:
So it's nicely printing out the version as I expect. But on Linux we get this:
>>> from subprocess import Popen
>>> Popen(['gcc', '--version'], shell=True)
gcc: no input files
因为gcc没有收到--version
选项.
Because gcc hasn't received the --version
option.
文档没有具体说明在 Windows 下 args 应该发生什么,但它确实说,在 Unix 上,如果 args 是一个序列,第一项指定命令字符串,任何附加项将被视为额外的 shell 参数." 恕我直言,Windows 方式更好,因为它允许您将 Popen(arglist)
调用与 Popen(arglist, shell=True)
个.
The docs don't specify exactly what should happen to the args under Windows, but it does say, on Unix, "If args is a sequence, the first item specifies the command string, and any additional items will be treated as additional shell arguments." IMHO the Windows way is better, because it allows you to treat Popen(arglist)
calls the same as Popen(arglist, shell=True)
ones.
为什么这里有 Windows 和 Linux 的区别?
解决方案
实际上在 Windows 上,当 shell=True
时它确实使用 cmd.exe
- 它预先添加 cmd.exe/c
(它实际上查找 COMSPEC
环境变量,但如果不存在则默认为 cmd.exe
)到 shell 参数.(在 Windows 95/98 上,它使用中间 w9xpopen
程序来实际启动命令.
Actually on Windows, it does use cmd.exe
when shell=True
- it prepends cmd.exe /c
(it actually looks up the COMSPEC
environment variable but defaults to cmd.exe
if not present) to the shell arguments. (On Windows 95/98 it uses the intermediate w9xpopen
program to actually launch the command).
所以奇怪的实现实际上是 UNIX
一个,它执行以下操作(每个空格分隔不同的参数):
So the strange implementation is actually the UNIX
one, which does the following (where each space separates a different argument):
/bin/sh -c gcc --version
看起来正确的实现(至少在 Linux 上)应该是:
It looks like the correct implementation (at least on Linux) would be:
/bin/sh -c "gcc --version" gcc --version
因为这会从引用的参数中设置命令字符串,并成功传递其他参数.
Since this would set the command string from the quoted parameters, and pass the other parameters successfully.
来自 -c
的 sh
手册页部分:
From the sh
man page section for -c
:
从 command_string 操作数而不是从标准输入读取命令.特殊参数 0 将从 command_name 操作数设置,位置参数($1、$2 等)从其余参数操作数设置.
这个补丁看起来很简单:
This patch seems to fairly simply do the trick:
--- 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]
相关文章