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,例如 bash
或 zsh
。如果你 运行 像这样的命令会发生什么?
$ /usr/bin/touch one two
发生的事情是 shell 解释您编写的命令并将其拆分为 whitespace 以创建数组 ["/usr/bin/touch", "one", "two"]
。然后它使用该参数列表启动一个新进程,在本例中创建两个名为 one
和 two
.
的文件
如果您想要一个名为 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 中,您可以将 subprocess
与 shell=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。)
我知道之前有人问过类似的问题,但它们似乎都已通过修改参数的传递方式(即使用列表等)得到解决。
但是,我在这里遇到一个问题,因为我没有那个选项。有一个特定的命令行程序(我正在使用 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,例如 bash
或 zsh
。如果你 运行 像这样的命令会发生什么?
$ /usr/bin/touch one two
发生的事情是 shell 解释您编写的命令并将其拆分为 whitespace 以创建数组 ["/usr/bin/touch", "one", "two"]
。然后它使用该参数列表启动一个新进程,在本例中创建两个名为 one
和 two
.
如果您想要一个名为 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 中,您可以将 subprocess
与 shell=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。)