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

时间:2015-06-16 18:09:46

标签: python subprocess

使用subprocess.Popen时,我们必须写

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

而不是

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

为什么呢? {2}在第二种情况下会得到什么?谢谢。

3 个答案:

答案 0 :(得分:3)

当您的操作系统启动可执行文件时,它会通过一个非常类似的调用来执行此操作:

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

请注意,在ls开始之前,参数已经被拆分为单个单词;如果你用shell运行你的程序,那么shell负责进行拆分;如果你是通过一种允许你直接控制execv调用参数的编程语言来运行它,那么你就决定如何自己拆分数组。

ls运行时,它会将这些参数传递给数组argv。见证在C:

中声明main函数的通常方式
int main(int argc, char *argv[]) {
  ...
}

在传统上名为argv的变量中,正在获取数组参数,已经分解为单个单词。

然后,ls的解析器可以预期,当它运行时,将会传递一个如下所示的数组:

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

...所以ls中内置的命令行解析器不需要拆分其参数内的空格 - 空格已被删除,语法引号在{{{{}}之前被尊重和删除1}}命令永远开始。

现在,当您运行ls时,您明确指定argc为2而不是3,并且单个参数包含单个字符串['ls', '-l -a']。要从shell中获取该行为,您需要使用引用或转义:

-l -a

...你会发现ls "-l -a" ls '-l -a' ls -l\ -a 失败的方式与从这里使用任何这些用法的shell调用的方式完全相同。

答案 1 :(得分:1)

在第二种情况下06-18 07:34:57.329: W/AmazonAppstore.AppManagerAndroidPackageDelegate(2325): Unable to determine asin for package com.MyGPCMobile. Skipping metadata update. 06-18 07:34:58.709: W/AmazonAppstore.AppManagerAndroidPackageDelegate(2325): Unable to determine asin for package com.MyGPCMobile. Skipping metadata update. 06-18 07:35:02.129: W/BindingManager(2501): Cannot call determinedVisibility() - never saw a connection for the pid: 2501 06-18 07:35:02.529: W/BindingManager(2501): Cannot call determinedVisibility() - never saw a connection for the pid: 2501 06-18 07:35:03.469: W/BindingManager(2501): Cannot call determinedVisibility() - never saw a connection for the pid: 2501 06-18 07:35:10.879: W/BindingManager(2501): Cannot call determinedVisibility() - never saw a connection for the pid: 2501 06-18 07:35:11.769: W/BindingManager(2501): Cannot call determinedVisibility() - never saw a connection for the pid: 2501 06-18 07:35:30.579: D/DMCFaceEngine(919): caSmartDP_DetermineFixedDevice : TmpFile exists /data/smart_stay_hash.dmc 06-18 07:35:30.579: D/DMCFaceEngine(919): caSmartDP_DetermineFixedDevice : FIXED OR NOT : difference 300.376667 06-18 07:35:30.579: D/DMCFaceEngine(919): caSmartDP_DetermineFixedDevice : TmpFile making start /data/smart_stay_hash.dmc 06-18 07:35:30.589: D/DMCFaceEngine(919): caSmartDP_DetermineFixedDevice : TmpFile making end /data/smart_stay_hash.dmc 06-18 07:35:30.589: D/DMCFaceEngine(919): caSmartDP_SmartStay : [5.000000] determine[300] result [-1] 06-18 07:35:35.919: W/BindingManager(2501): Cannot call determinedVisibility() - never saw a connection for the pid: 2501 06-18 07:35:35.929: W/BindingManager(2501): Cannot call determinedVisibility() - never saw a connection for the pid: 2501 06-18 07:35:38.739: W/BindingManager(2501): Cannot call determinedVisibility() - never saw a connection for the pid: 2501 06-18 07:35:39.689: W/BindingManager(2501): Cannot call determinedVisibility() - never saw a connection for the pid: 2501 06-18 07:35:39.699: W/BindingManager(2501): Cannot call determinedVisibility() - never saw a connection for the pid: 2501 作为单个字符串将是-l -a的第一个参数,它不知道该怎么做,或者至少不会做你想要的。在第一种情况下,ls是第一个参数,-l是第二个参数。

如果你想构建一个包含完整命令的字符串,你可以使用-a标志给Popen,但是你的命令是shell=True而不是"ls -l -a"

使用['ls', '-l -a']列表中的每个参数都是传递给正在执行的命令的参数,它不是传递给要解释的shell的字符串,除非您要求将其传递给要解释的shell

答案 2 :(得分:0)

如果要使用命令的字符串表示来执行,shlex模块可能很有用。

  

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

     

使用类似shell的语法拆分字符串s。如果comments为False(默认值),则解析给定字符串中的注释   disabled(将shlex实例的commenters属性设置为   空字符串)。默认情况下,此功能在POSIX模式下运行,   但如果posix参数为false,则使用非POSIX模式。

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

它还涵盖了更复杂的案例,例如转义字符或引号用法:

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']