使用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}在第二种情况下会得到什么?谢谢。
答案 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']