Python 的子流程不允许使用 shell=True 进行流程替换?
Process substitution not allowed by Python's subprocess with shell=True?
这是一个进程替换的玩具示例,在 Bash 中运行良好:
$ wc -l <(pwd)
1 /proc/self/fd/11
那么,为什么从 Python 的子进程调用 shell=True 时,相同的命令会出现语法错误?
>>> subprocess.check_call('wc -l <(pwd)', shell=True)
/bin/sh: 1: Syntax error: "(" unexpected
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "/path/to/my/python/lib/python3.5/subprocess.py", line 581, in check_call
raise CalledProcessError(retcode, cmd)
subprocess.CalledProcessError: Command 'wc -l <(pwd)' returned non-zero exit status 2
/bin/sh: 1: Syntax error: "(" unexpected
你有一个bashism。根据 POSIX,这是无效的,这是 /bin/sh
实现的。
另一种解决方案是将更多 shell 代码转移到 Python 本身。例如:
from subprocess import Popen, PIPE, check_call
p1 = Popen(["pwd"], stdout=PIPE)
p2 = check_call(["wc", "-l"], stdin=p1.stdout)
这通常是消除使用 subprocess
的第一步,因为它将工作分解为更小的块,您可能更容易在 Python 中看到如何做本身。
如果你想使用Bash特性(数组,命令替换,这里是字符串,or a lot of other non-POSIX extensions and enhancements),你需要显式覆盖默认的shell:
subprocess.check_call(
'wc -l <(pwd)',
executable='/bin/bash', # the beef
shell=True)
或者 - 更笨拙 - 运行 一个明确的 Bash 实例:
subprocess.check_call(
['/bin/bash', '-c', 'wc -l <(pwd)'])
注意在后一种情况下我们如何避免单独指定 shell=True
,并将脚本作为字符串列表传递(其中第三个字符串是任意复杂的 and/or 长脚本作为参数至 bash -c
).
(实际上有一个长度限制。如果你的命令行比内核常量长 ARG_MAX
你需要将脚本传递到文件中或作为标准输入到 shell相反。在任何现代系统上,我们都在谈论兆字节的脚本。)
无论如何,运行 复杂的 shell 脚本(Bash 或其他)是可疑的;您将希望尽可能少地委派给 subprocess
并以原生 Python 代码从那里获取它。
这是一个进程替换的玩具示例,在 Bash 中运行良好:
$ wc -l <(pwd)
1 /proc/self/fd/11
那么,为什么从 Python 的子进程调用 shell=True 时,相同的命令会出现语法错误?
>>> subprocess.check_call('wc -l <(pwd)', shell=True)
/bin/sh: 1: Syntax error: "(" unexpected
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "/path/to/my/python/lib/python3.5/subprocess.py", line 581, in check_call
raise CalledProcessError(retcode, cmd)
subprocess.CalledProcessError: Command 'wc -l <(pwd)' returned non-zero exit status 2
/bin/sh: 1: Syntax error: "(" unexpected
你有一个bashism。根据 POSIX,这是无效的,这是 /bin/sh
实现的。
另一种解决方案是将更多 shell 代码转移到 Python 本身。例如:
from subprocess import Popen, PIPE, check_call
p1 = Popen(["pwd"], stdout=PIPE)
p2 = check_call(["wc", "-l"], stdin=p1.stdout)
这通常是消除使用 subprocess
的第一步,因为它将工作分解为更小的块,您可能更容易在 Python 中看到如何做本身。
如果你想使用Bash特性(数组,命令替换,这里是字符串,or a lot of other non-POSIX extensions and enhancements),你需要显式覆盖默认的shell:
subprocess.check_call(
'wc -l <(pwd)',
executable='/bin/bash', # the beef
shell=True)
或者 - 更笨拙 - 运行 一个明确的 Bash 实例:
subprocess.check_call(
['/bin/bash', '-c', 'wc -l <(pwd)'])
注意在后一种情况下我们如何避免单独指定 shell=True
,并将脚本作为字符串列表传递(其中第三个字符串是任意复杂的 and/or 长脚本作为参数至 bash -c
).
(实际上有一个长度限制。如果你的命令行比内核常量长 ARG_MAX
你需要将脚本传递到文件中或作为标准输入到 shell相反。在任何现代系统上,我们都在谈论兆字节的脚本。)
运行 复杂的 shell 脚本(Bash 或其他)是可疑的;您将希望尽可能少地委派给 subprocess
并以原生 Python 代码从那里获取它。