我尝试做
import subprocess
p = subprocess.Popen("ls -la /etc", stdout=subprocess.PIPE, stderr=subprocess.PIPE)
p.stdout.read().decode()
哪个给我
FileNotFoundError: [Errno 2] No such file or directory: 'ls -la /etc': 'ls -la /etc'
关注中
Python subprocess.Popen with var/args
我做了
import subprocess
p = subprocess.Popen(["ls", "-la", "/etc"], stdout=subprocess.PIPE, stderr=subprocess.PIPE)
p.stdout.read().decode()
哪个工作了。
为什么?为什么必须拆分命令及其参数?这种设计背后的原理是什么?
Python版本:
3.7.3 (default, Mar 27 2019, 22:11:17)
[GCC 7.3.0]
根据docs,它取决于shell=
关键字参数关于字符串与列表的工作方式(粗体表示可能导致您经历的行为的原因:]
args应该是程序参数的序列,或者是单个字符串或类似路径的对象。默认情况下,如果args是序列,则要执行的程序是args中的第一项。如果args是字符串,则解释取决于平台,并在下面进行描述。有关默认行为的其他区别,请参见shell和可执行参数。除非另有说明,否则建议将args作为序列传递。
在POSIX上,如果args是字符串,则该字符串将解释为要执行的程序的名称或路径。但是,只有在不将参数传递给程序的情况下才能执行此操作。
进一步向下...
在具有shell = True的POSIX上,shell默认为/ bin / sh。如果args是字符串,则该字符串指定要通过外壳执行的命令。这意味着字符串的格式必须与在shell提示符下键入时的格式完全相同。例如,这包括在文件名中使用引号或反斜杠转义。如果args是序列,则第一项指定命令字符串,任何其他项都将被视为shell本身的其他参数。也就是说,Popen等效于:
Popen(['/bin/sh', '-c', args[0], args[1], ...])
在具有shell = True的Windows上,COMSPEC环境变量指定默认的shell。在Windows上唯一需要指定shell = True的时间是将要执行的命令内置到shell中(例如dir或copy)。您不需要shell = True即可运行批处理文件或基于控制台的可执行文件。
幕后,通常在UNIX上运行程序要执行以下步骤:
fork()
关闭子进程。execve()
syscall将当前进程替换为所需的子进程。 此系统调用采用参数数组,而不是单个字符串。wait()
,让孩子退出,如果要阻止该电话。因此,subprocess.Popen
公开了数组接口,因为数组接口是操作系统实际上在后台执行的操作。
在外壳上运行ls /tmp
时,该外壳将字符串转换为数组-但可以提供更多控制权(避免严重的错误-如果有人创建名为/tmp/$(rm -rf ~)
的文件,您不想在自己进行转换时尝试cat /tmp/$(rm -rf ~)
删除您的主目录。