Python 子流程:无法转义引号

Python Subprocess: Unable to Escape Quotes

我知道之前有人问过类似的问题,但它们似乎都已通过修改参数的传递方式(即使用列表等)得到解决。

但是,我在这里遇到一个问题,因为我没有那个选项。有一个特定的命令行程序(我正在使用 Bash shell),我必须向其传递一个带引号的字符串。它不能不加引号,不能有重复的参数,它必须是单引号或双引号。

command -flag 'foo foo1'

我不能使用command -flag foo foo1,我也不能使用command -flag foo -flag foo1。我认为这是对命令接收输入的编程方式的疏忽,但我无法控制它。

我传递参数如下:

self.commands = [
                self.path,
                '-flag1', quoted_argument,
                '-flag2', 'test',
                ...etc...
                ]
process = subprocess.Popen(self.commands, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
results = process.communicate(input)

其中 quoted_argument 类似于 'foo foo1 foo2'。 我尝试转义单引号 ("\'foo foo1 foo2\'"),但没有输出。

我知道这被认为是不好的做法,因为它的解释不明确,但我别​​无选择。有什么想法吗?

shell 将命令字符串分成列表。引号告诉 shell 将多个单词放入单个列表项中。由于您是自己构建列表,因此您将单词添加为不带引号的单个项目。

这两个Popen命令是等价的

Popen("command -flag 'foo foo1'", shell=True)
Popen(["command", "-flag", "foo foo1"])

编辑

这个答案处理 shell 中的转义字符。如果不使用 shell,则不添加任何引号或转义符,只需放入字符串本身。跳过 shell 还存在其他问题,例如管道命令、运行 后台作业、使用 shell 变量等。这些都可以在 python 中完成,而不是 shell.

我发现非常有用的过程和 shell 的心智模型:

这些年来,这种思维模式对我帮助很大。

您操作系统中的进程接收一个表示参数的字符串数组。在 Python 中,可以从 sys.argv 访问此数组。在 C 中,这是传递给 main 函数的 argv 数组。等等。

当您打开一个终端时,您在该终端内 运行 设置了一个 shell,例如 bashzsh。如果你 运行 像这样的命令会发生什么?

$ /usr/bin/touch one two

发生的事情是 shell 解释您编写的命令并将其拆分为 whitespace 以创建数组 ["/usr/bin/touch", "one", "two"]。然后它使用该参数列表启动一个新进程,在本例中创建两个名为 onetwo.

的文件

如果您想要一个名为 one two 且带有 space 的文件怎么办?您不能像您想要的那样向 shell 传递一个参数列表,您只能向它传递一个字符串。 Bash 和 Zsh 等 Shell 使用单引号来解决这个问题:

$ /usr/bin/touch 'one two'

shell 将使用参数 ["/usr/bin/touch", "one two"] 创建一个新进程,在本例中创建一个名为 one two.

的文件

贝壳具有管道等特殊功能。使用 shell,您可以这样做:

$ /usr/bin/echo 'This is an example' | /usr/bin/tr a-z A-Z
THIS IS AN EXAMPLE

在这种情况下,shell 对 | 字符的解释不同。 In 创建一个参数为 ["/usr/bin/echo", "This is an example"] 的进程和另一个参数为 ["/usr/bin/tr", "a-z", "A-Z"] 的进程,并将前者的输出通过管道传输到后者的输入。

这如何适用于 Python

中的 subprocess

现在,在 Python 中,您可以将 subprocessshell=False 一起使用(这是默认设置,或者与 shell=True 一起使用。如果您使用默认行为 shell=False,然后 subprocess 期望您将参数列表传递给它。您不能使用特殊的 shell 功能,例如 shell 管道。从好的方面来说,您不必担心关于转义 shell:

的特殊字符
import subprocess
# create a file named "one two"
subprocess.call(["/usr/bin/touch", "one two"])

如果您确实想使用 shell 功能,您可以这样做:

subprocess.call(
    "/usr/bin/echo 'This is an example' | /usr/bin/tr a-z A-Z",
    shell=True,
)

如果您使用的变量没有特别保证,请记住转义命令:

import shlex
import subprocess

subprocess.call(
    "/usr/bin/echo " + shlex.quote(variable) + " | /usr/bin/tr a-z A-Z",
    shell=True,
)

(请注意,shlex.quote 仅适用于 UNIX shells,不适用于 Windows 上的 DOS。)