这是一个过程替换的玩具示例,在Bash中运行良好:
$ wc -l <(pwd)
1 /proc/self/fd/11
那么为什么同样的命令在使用shell = True从Python的子进程调用时会出现语法错误?
>>> 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
答案 0 :(得分:2)
/ bin / sh:1:语法错误:“(”意外
你有bashism。根据POSIX,它是无效的,这是/bin/sh
实现的。
答案 1 :(得分:2)
另一种解决方案是将更多的shell代码转移到Python本身。例如:
from subprocess import Popen, PIPE, check_call
p1 = Popen(["pwd"], stdout=PIPE)
p2 = check_call(["wc", "-l"], stdin=p1.stdout)
这通常是消除使用subprocess
的必要性的第一步,因为它将工作分解为更小的块,您可能更容易看到如何使用Python本身。
答案 2 :(得分:0)
如果你想使用 Bash 特性(数组、命令替换、here 字符串、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
,并将脚本作为字符串列表传入(其中第三个字符串是任意复杂和/或长的脚本作为 {{1} }}).
(实际上有长度限制。如果您的命令行比内核常量 bash -c
长,您需要将脚本以文件形式或作为标准输入传递给 shell。在任何现代系统上,不过,我们正在谈论数兆字节的脚本。)
无论如何,从 Python 运行复杂的 shell 脚本(Bash 或其他)是可疑的;您会希望将尽可能少的委派给 ARG_MAX
并在本机 Python 代码中使用它。