子进程中‘shell=True’的实际含义

2024-11-18 08:41:00
admin
原创
11
摘要:问题描述:我正在用模块调用不同的进程subprocess。但是,我有一个问题。在下面的代码中:callProcess = subprocess.Popen(['ls', '-l'], shell=True) 和callProcess = subprocess.Popen(['ls', '-l']) # wit...

问题描述:

我正在用模块调用不同的进程subprocess。但是,我有一个问题。

在下面的代码中:

callProcess = subprocess.Popen(['ls', '-l'], shell=True)

callProcess = subprocess.Popen(['ls', '-l']) # without shell

两种方法都可以。阅读文档后,我才知道这shell=True意味着通过 shell 执行代码。也就是说,在没有 shell 的情况下,进程会直接启动。

那么对于我的情况我应该选择什么 - 我需要运行一个进程并获取其输出。从 shell 内部还是外部调用它有什么好处?


解决方案 1:

不通过 shell 调用的好处是,您不会调用“神秘程序”。在 POSIX 上,环境变量SHELL控制哪个二进制文件被调用为“shell”。在 Windows 上,没有 bourne shell 后代,只有 cmd.exe。

因此,调用 shell 会调用用户选择的程序,并且与平台相关。一般来说,应避免通过 shell 进行调用。

通过 shell 调用确实允许您根据 shell 的常用机制扩展环境变量和文件全局变量。在 POSIX 系统上,shell 将文件全局变量扩展为文件列表。在 Windows 上,shell 无论如何都不会扩展文件全局变量(例如“.”)(但命令行上的环境变量由 cmd.exe扩展)。

如果您认为需要环境变量扩展和文件全局,请研究ILS1992 年左右对网络服务的攻击,这些攻击通过 shell 执行子程序调用。示例包括sendmail涉及 的各种后门ILS

总之,使用shell=False

解决方案 2:

>>> import subprocess
>>> subprocess.call('echo $HOME')
Traceback (most recent call last):
...
OSError: [Errno 2] No such file or directory
>>>
>>> subprocess.call('echo $HOME', shell=True)
/user/khong
0

将 shell 参数设置为 true 值会导致 subprocess 生成一个中间 shell 进程,并告诉它运行该命令。换句话说,使用中间 shell 意味着在运行命令之前会处理命令字符串中的变量、glob 模式和其他特殊 shell 功能。在此示例中,$HOME 在 echo 命令之前被处理。实际上,这是带有 shell 扩展的命令的情况,而命令 ls -l 被视为简单命令。

来源:子流程模块

解决方案 3:

这里显示了 Shell=True 可能出错的示例

>>> from subprocess import call
>>> filename = input("What file would you like to display?
")
What file would you like to display?
non_existent; rm -rf / # THIS WILL DELETE EVERYTHING IN ROOT PARTITION!!!
>>> call("cat " + filename, shell=True) # Uh-oh. This will end badly...

在此处查看文档:subprocess.call()

解决方案 4:

通过 shell 执行程序意味着传递给程序的所有用户输入都将根据所调用 shell 的语法和语义规则进行解释。在最好的情况下,这只会给用户带来不便,因为用户必须遵守这些规则。例如,必须对包含引号或空格等特殊 shell 字符的路径进行转义。在最坏的情况下,这会导致安全漏洞,因为用户可以执行任意程序。

shell=True有时使用特定的 shell 功能(如分词或参数扩展)会很方便。但是,如果需要这样的功能,请使用提供给您的其他模块(例如os.path.expandvars()用于参数扩展或shlex分词)。这意味着更多的工作,但可以避免其他问题。

简而言之:shell=True一定要避免。

解决方案 5:

这里的其他答案充分解释了文档中提到的安全注意事项subprocess。但除此之外,启动 shell 来启动要运行的程序的开销通常是不必要的,而且对于您实际上不使用任何 shell 功能的情况来说绝对是愚蠢的。此外,额外的隐藏复杂性应该会让您感到害怕,特别是如果您不太熟悉 shell 或它提供的服务。

当与 shell 的交互并不简单时,您现在需要 Python 脚本的读者和维护者(可能是也可能不是您自己)了解 Python 和 shell 脚本。请记住 Python 的座右铭“显式优于隐式”;即使 Python 代码比等效(通常非常简洁)的 shell 脚本稍微复杂一些,您最好还是删除 shell 并用本机 Python 构造替换功能。尽量减少在外部进程中完成的工作并尽可能在您自己的代码中保持控制通常是一个好主意,因为它可以提高可见性并降低出现(想要的或不想要的)副作用的风险。

通配符扩展、变量插值和重定向都很容易用原生 Python 结构替换。如果某个复杂的 shell 管道的部分或全部无法用 Python 合理地重写,那么也许您可以考虑使用 shell。您仍应确保了解性能和安全影响。

在简单情况下,为了避免shell=True,只需替换

subprocess.Popen("command -with -options 'like this' and\\ an\\ argument", shell=True)

subprocess.Popen(['command', '-with','-options', 'like this', 'and an argument'])

请注意,第一个参数是传递给的字符串列表execvp(),以及引用字符串和反斜杠转义的 shell 元字符通常不是必需的(或有用的,或正确的)。也许还可以参阅何时将引号括在 shell 变量周围?

如果您不想自己弄清楚,该shlex.split()函数可以为您完成此操作。它是 Python 标准库的一部分,但当然,如果您的 shell 命令字符串是静态的,您可以在开发过程中运行一次,然后将结果粘贴到脚本中。

另外,Popen如果包中较简单的包装器之一subprocess满足您的要求,您通常希望避免这种情况。如果您的 Python 版本足够新,则可能应该使用subprocess.run

  • check=True如果您运行的命令失败,它也会失败。

  • 它将stdout=subprocess.PIPE捕获命令的输出。

  • 使用text=True(或者有点晦涩,使用同义词universal_newlines=True),它会将输出解码为正确的 Unicode 字符串(bytes否则,在 Python 3 上,它只是系统编码)。

如果不是,对于许多任务来说,您希望check_output获取命令的输出,同时检查它是否成功,或者check_call是否没有要收集的输出。

最后,我想引用 David Korn 的一句话:“编写可移植的 shell 比编写可移植的 shell 脚本更容易。” 即使subprocess.run('echo "$HOME"', shell=True)无法移植到 Windows。

解决方案 6:

上面的答案解释得没错,但不够直接。让我们使用ps命令看看会发生什么。

import time
import subprocess

s = subprocess.Popen(["sleep 100"], shell=True)
print("start")
print(s.pid)
time.sleep(5)
s.kill()
print("finish")

运行它,并显示

start
832758
finish

然后你可以使用ps -auxf > 1before finish,然后是ps -auxf > 2after finish。以下是输出

1

cy         71209  0.0  0.0   9184  4580 pts/6    Ss   Oct20   0:00  |       _ /bin/bash
cy        832757  0.2  0.0  13324  9600 pts/6    S+   19:31   0:00  |       |   _ python /home/cy/Desktop/test.py
cy        832758  0.0  0.0   2616   612 pts/6    S+   19:31   0:00  |       |       _ /bin/sh -c sleep 100
cy        832759  0.0  0.0   5448   532 pts/6    S+   19:31   0:00  |       |           _ sleep 100

sleep 100看到了吗?它实际上运行的是,/bin/sh而不是直接运行。pid它打印出来的实际上是pid/bin/sh之后,如果你调用s.kill(),它会被杀死/bin/sh,但sleep仍然存在。

2

cy         69369  0.0  0.0 533764  8160 ?        Ssl  Oct20   0:12  _ /usr/libexec/xdg-desktop-portal
cy         69411  0.0  0.0 491652 14856 ?        Ssl  Oct20   0:04  _ /usr/libexec/xdg-desktop-portal-gtk
cy        832646  0.0  0.0   5448   596 pts/6    S    19:30   0:00  _ sleep 100

那么下一个问题是,它能做什么/bin/sh?每个 Linux 用户都知道它、听说过它并使用它。但我敢打赌,有很多人并不真正了解它shell到底是什么。也许你也听说过/bin/bash,它们很相似。

shell 的一个明显功能就是方便用户运行 linux 应用程序。因为有像sh或 这样的 shell 程序bash,您可以直接使用像 这样的命令ls而不是/usr/bin/ls。它会搜索 在哪里ls并为您运行它。

另一个功能是它将后面的字符串解释$为环境变量。你可以比较这两个 python 脚本来自己找出答案。

subprocess.call(["echo $PATH"], shell=True)
subprocess.call(["echo", "$PATH"])

最重要的是,它可以将 Linux 命令作为脚本运行。例如if else由 shell 引入的命令。它不是原生的 Linux 命令

解决方案 7:

假设您使用 shell=False 并以列表形式提供命令。一些恶意用户尝试注入“rm”命令。您将看到,“rm”将被解释为参数,并且实际上“ls”将尝试查找名为“rm”的文件

>>> subprocess.run(['ls','-ld','/home','rm','/etc/passwd'])
ls: rm: No such file or directory
-rw-r--r--    1 root     root          1172 May 28  2020 /etc/passwd
drwxr-xr-x    2 root     root          4096 May 29  2020 /home
CompletedProcess(args=['ls', '-ld', '/home', 'rm', '/etc/passwd'], returncode=1)

如果您没有正确控制输入,则默认情况下 shell=False 并不安全。您仍然可以执行危险的命令。

>>> subprocess.run(['rm','-rf','/home'])
CompletedProcess(args=['rm', '-rf', '/home'], returncode=0)
>>> subprocess.run(['ls','-ld','/home'])
ls: /home: No such file or directory
CompletedProcess(args=['ls', '-ld', '/home'], returncode=1)
>>>

我在容器环境中编写了大多数应用程序,我知道哪个 shell 被调用,并且我没有接受任何用户输入。

因此,在我的使用案例中,我没有看到任何安全风险。而且创建长串命令要容易得多。希望我没有错。

相关推荐
  为什么项目管理通常仍然耗时且低效?您是否还在反复更新电子表格、淹没在便利贴中并参加每周更新会议?这确实是耗费时间和精力。借助软件工具的帮助,您可以一目了然地全面了解您的项目。如今,国内外有足够多优秀的项目管理软件可以帮助您掌控每个项目。什么是项目管理软件?项目管理软件是广泛行业用于项目规划、资源分配和调度的软件。它使项...
项目管理软件   601  
  华为IPD与传统研发模式的8大差异在快速变化的商业环境中,产品研发模式的选择直接决定了企业的市场响应速度和竞争力。华为作为全球领先的通信技术解决方案供应商,其成功在很大程度上得益于对产品研发模式的持续创新。华为引入并深度定制的集成产品开发(IPD)体系,相较于传统的研发模式,展现出了显著的差异和优势。本文将详细探讨华为...
IPD流程是谁发明的   7  
  如何通过IPD流程缩短产品上市时间?在快速变化的市场环境中,产品上市时间成为企业竞争力的关键因素之一。集成产品开发(IPD, Integrated Product Development)作为一种先进的产品研发管理方法,通过其结构化的流程设计和跨部门协作机制,显著缩短了产品上市时间,提高了市场响应速度。本文将深入探讨如...
华为IPD流程   9  
  在项目管理领域,IPD(Integrated Product Development,集成产品开发)流程图是连接创意、设计与市场成功的桥梁。它不仅是一个视觉工具,更是一种战略思维方式的体现,帮助团队高效协同,确保产品按时、按质、按量推向市场。尽管IPD流程图可能初看之下显得错综复杂,但只需掌握几个关键点,你便能轻松驾驭...
IPD开发流程管理   8  
  在项目管理领域,集成产品开发(IPD)流程被视为提升产品上市速度、增强团队协作与创新能力的重要工具。然而,尽管IPD流程拥有诸多优势,其实施过程中仍可能遭遇多种挑战,导致项目失败。本文旨在深入探讨八个常见的IPD流程失败原因,并提出相应的解决方法,以帮助项目管理者规避风险,确保项目成功。缺乏明确的项目目标与战略对齐IP...
IPD流程图   8  
热门文章
项目管理软件有哪些?
云禅道AD
禅道项目管理软件

云端的项目管理软件

尊享禅道项目软件收费版功能

无需维护,随时随地协同办公

内置subversion和git源码管理

每天备份,随时转为私有部署

免费试用