简体   繁体   English

为什么我们不能在subprocess.Popen中合并参数?

[英]Why can't we combine arguments in subprocess.Popen?

When using subprocess.Popen , we have to write 使用subprocess.Popen ,我们必须编写

with subprocess.Popen(['ls', '-l', '-a'], stdout=subprocess.PIPE) as proc:
    print(proc.stdout.read())

instead of 代替

with subprocess.Popen(['ls', '-l -a'], stdout=subprocess.PIPE) as proc:
    print(proc.stdout.read())

Why? 为什么? What ls will get in the second case? 什么ls将在第二种情况下得到什么? Thank you. 谢谢。

When your operating system starts an executable, it does this via a call something very much like this: 当您的操作系统启动可执行文件时,它通过类似于以下的调用来执行此操作:

execv('/usr/bin/ls', 'ls', '-l', '-a', NULL)

Note that the arguments are already split out into individual words before ls is started; 注意,在ls开始之前,参数已被拆分为单个单词; if you're running your program with a shell, then the shell is responsible for doing that splitting; 如果您使用外壳运行程序,则外壳负责进行拆分; if you're running it via a programming language that lets you control the execv call's arguments directly, then you're deciding how to split the array up yourself. 如果通过允许您直接控制execv调用参数的编程语言运行它,那么您将决定如何自己拆分数组。

When ls runs, it's passed those arguments in an array, argv . ls运行时,它会将这些参数传递给数组argv Witness the usual way a main function is declared in C: 见证在C中声明main功能的常用方法:

int main(int argc, char *argv[]) {
  ...
}

It's getting an array of arguments, in a variable conventionally named argv , already broken up into individual words. 它在一个通常称为argv的变量中获取了一参数,这些参数已经分解成单个单词。

The parser for ls , then, can expect that when it's run it will be handed an array that looks like this: 然后, ls的解析器可以期望在运行时将其传递给如下所示的数组:

argc = 3                   # three arguments, including our own name
argv = ['ls', '-l', '-a']  # first argument is our name, others follow

...so the command-line parser built into ls doesn't need to break up spaces inside of its arguments -- spaces have already been removed, and syntactic quotes honored and stripped, before the ls command is ever started. ...因此,内置于ls的命令行解析器不需要在其参数内拆分空格-在启动ls命令之前,空格已被删除,并且语法引号已被接受和剥离。

Now, when you run ['ls', '-l -a'] , you're explicitly specifying an argc of 2, not 3, and a single argument that includes a single string -l -a . 现在,当您运行['ls', '-l -a'] ,您将显式指定一个argc为2,而不是3,以及一个包含单个字符串-l -a的单个参数。 To get that behavior from a shell, you'd need to use quoting or escaping: 要从外壳程序获得该行为,您需要使用引号或转义符:

ls "-l -a"
ls '-l -a'
ls -l\ -a

...and you'll find that ls fails the exact same way as what you get here when invoked from a shell with any of those usages. ...并且您会发现ls失败的方式与您从具有任何这些用法的shell调用时得到的完全相同。

In the second case -l -a as a single string will be the first argument to ls , which it won't know what to do with, or at least won't do what you want. 在第二种情况下, -l -a作为单个字符串将是ls的第一个参数,它将不知道该如何处理,或者至少不执行您想要的操作。 In the first case -l is the first argument and -a is the second argument. 在第一种情况下, -l是第一个参数, -a是第二个参数。

If you want to build a string that has the complete command you can use the shell=True flag to Popen, but then your command would be "ls -l -a" not ['ls', '-l -a'] 如果要构建具有完整命令的字符串,可以使用shell=True标志来Popen,但是您的命令将是"ls -l -a"而不是['ls', '-l -a']

With Popen each argument in the list is an argument passed to the command being executed, it's not a string passed to the shell to be interpreted, unless you ask for it to be passed to the shell to be interpreted. 使用Popen ,列表中的每个参数都是传递给正在执行的命令的参数,它不是传递给要解释的shell的字符串,除非您要求将其传递给要解释的shell。

If you want to use string representation of command to execute, shlex module may be useful. 如果要使用命令的字符串表示形式执行,则shlex模块可能会有用。

shlex.split(s[, comments[, posix]]) shlex.split(s [,comments [,posix]])

Split the string s using shell-like syntax. 使用类似于shell的语法拆分字符串。 If comments is False (the default), the parsing of comments in the given string will be disabled (setting the commenters attribute of the shlex instance to the empty string). 如果注释为False(默认值),则将禁用对给定字符串中注释的解析(将shlex实例的commenters属性设置为空字符串)。 This function operates in POSIX mode by default, but uses non-POSIX mode if the posix argument is false. 默认情况下,此函数在POSIX模式下运行,但如果posix参数为false,则使用非POSIX模式。

assert shlex.split("ls -a -l") == ['ls', '-a', '-l']
subprocess.Popen(shlex.split("ls -a -l"))

It also covers more complex cases like escaping chars or quotes usage: 它还涵盖了更复杂的情况,例如转义字符或引号的用法:

assert shlex.split("cat 'file with space.txt'") == ['cat', 'file with space.txt']
assert shlex.split(r"cat file\ with\ space.txt") == ['cat', 'file with space.txt']

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM