0

这是一个在 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
4

3 回答 3

2

/bin/sh: 1: 语法错误: "(" 意外

你有一个bashism。根据实现的 POSIX,它是无效的/bin/sh

于 2016-09-30T17:52:15.723 回答
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 本身中完成这些块。

于 2016-09-30T18:27:58.523 回答
0

如果您想使用 Bash 功能(数组、命令替换、此处为字符串或许多其他非 POSIX 扩展和增强功能),您需要显式覆盖默认 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,并将脚本作为字符串列表传递(其中第三个字符串是任意复杂和/或长脚本作为 的参数bash -c)。

(实际上有一个长度限制。如果您的命令行比内核常量长,ARG_MAX您需要将脚本传递到文件中或作为标准输入传递给 shell。在任何现代系统上,我们说的是兆字节的脚本,尽管。)

无论如何,从 Python 运行复杂的 shell 脚本(Bash 或其他)是值得怀疑的;您将希望尽可能少地委派给 asubprocess并在本机 Python 代码中从那里获取它。

于 2021-01-04T17:42:19.603 回答