使用shell = True的Python子进程不允许进程替换?

时间:2016-09-30 17:47:18

标签: python bash shell subprocess process-substitution

这是一个过程替换的玩具示例,在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

3 个答案:

答案 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 代码中使用它。