【问题标题】:Why does the command and its arguments have to be in a list for subprocess.Popen?为什么命令及其参数必须在 subprocess.Popen 的列表中?
【发布时间】:2020-02-14 19:38:57
【问题描述】:

我试过了

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]

【问题讨论】:

  • 我见过的一个约定是传递你的字符串,但是split 它变成了一个列表:"ls -la /etc".split(' ')
  • 将参数作为列表给出,也可以包含变量和表达式作为项,允许底层代码确保正确引用并防止shell代码注入漏洞。
  • 有趣的是你如何使用/etc 并且错误抱怨~/projects...
  • wups更改目录后忘记重新运行代码
  • @KlausD.,“确保正确引用”?它完全阻止了任何需要的引用; shell 引用是一个 shell 结构;使用shell=False,您只是传递了一个文字 C 字符串列表,因此不需要正确的 不正确的引用。

标签: python linux subprocess


【解决方案1】:

这就是 所有 进程调用在 UNIX 上的工作方式。

在 UNIX 上运行程序通常通过以下步骤完成:

  1. fork() 关闭子进程。
  2. 在该子进程中,如果请求重定向,则打开 stdin、stdout、stderr 等的新副本,使用 dup2() 调用将新打开的文件分配到作为重定向目标的文件描述符上。
  3. 在该子进程中,使用execve() 系统调用将当前进程替换为所需的子进程。 此系统调用采用参数数组,而不是单个字符串。
  4. wait() 让孩子退出,如果呼叫是为了阻塞。

所以,subprocess.Popen 公开了数组接口,因为数组接口是操作系统在后台实际执行的操作

当您在 shell 中运行 ls /tmp 时,那个 shell 会将字符串转换为数组,然后自行执行上述步骤 - 但它为您提供了更多控制权(并避免了严重的错误 -如果有人创建了一个名为 /tmp/$(rm -rf ~) 的文件,那么当您自己进行转换时,您不想尝试 cat /tmp/$(rm -rf ~) 删除您的主目录。

【讨论】:

    【解决方案2】:

    根据docs,它取决于shell= 关键字参数,关于字符串与列表的工作方式(粗体表示可能导致您遇到的行为的原因):

    args 应该是一系列程序参数,或者是单个字符串或类似路径的对象。默认情况下,如果 args 是一个序列,则要执行的程序是 args 中的第一项。如果 args 是字符串,则解释取决于平台,如下所述。有关与默认行为的其他差异,请参阅 shell 和可执行参数。除非另有说明,否则建议将 args 作为序列传递。

    在 POSIX 上,如果 args 是字符串,则该字符串被解释为要执行的程序的名称或路径。但是,只有在不向程序传递参数的情况下才能做到这一点。

    再往下……

    在 shell=True 的 POSIX 上,shell 默认为 /bin/sh。如果 args 是字符串,则该字符串指定要通过 shell 执行的命令。这意味着字符串的格式必须与在 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 来运行批处理文件或基于控制台的可执行文件。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2023-04-07
      • 1970-01-01
      • 2011-12-03
      • 2021-05-15
      • 2019-02-19
      • 1970-01-01
      • 1970-01-01
      • 2012-01-26
      相关资源
      最近更新 更多