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

2024-11-18 08:41:00
admin
原创
329
摘要:问题描述:我正在用模块调用不同的进程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 被调用,并且我没有接受任何用户输入。

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

相关推荐
  政府信创国产化的10大政策解读一、信创国产化的背景与意义信创国产化,即信息技术应用创新国产化,是当前中国信息技术领域的一个重要发展方向。其核心在于通过自主研发和创新,实现信息技术应用的自主可控,减少对外部技术的依赖,并规避潜在的技术制裁和风险。随着全球信息技术竞争的加剧,以及某些国家对中国在科技领域的打压,信创国产化显...
工程项目管理   1565  
  为什么项目管理通常仍然耗时且低效?您是否还在反复更新电子表格、淹没在便利贴中并参加每周更新会议?这确实是耗费时间和精力。借助软件工具的帮助,您可以一目了然地全面了解您的项目。如今,国内外有足够多优秀的项目管理软件可以帮助您掌控每个项目。什么是项目管理软件?项目管理软件是广泛行业用于项目规划、资源分配和调度的软件。它使项...
项目管理软件   1354  
  信创国产芯片作为信息技术创新的核心领域,对于推动国家自主可控生态建设具有至关重要的意义。在全球科技竞争日益激烈的背景下,实现信息技术的自主可控,摆脱对国外技术的依赖,已成为保障国家信息安全和产业可持续发展的关键。国产芯片作为信创产业的基石,其发展水平直接影响着整个信创生态的构建与完善。通过不断提升国产芯片的技术实力、产...
国产信创系统   21  
  信创生态建设旨在实现信息技术领域的自主创新和安全可控,涵盖了从硬件到软件的全产业链。随着数字化转型的加速,信创生态建设的重要性日益凸显,它不仅关乎国家的信息安全,更是推动产业升级和经济高质量发展的关键力量。然而,在推进信创生态建设的过程中,面临着诸多复杂且严峻的挑战,需要深入剖析并寻找切实可行的解决方案。技术创新难题技...
信创操作系统   27  
  信创产业作为国家信息技术创新发展的重要领域,对于保障国家信息安全、推动产业升级具有关键意义。而国产芯片作为信创产业的核心基石,其研发进展备受关注。在信创国产芯片的研发征程中,面临着诸多复杂且艰巨的难点,这些难点犹如一道道关卡,阻碍着国产芯片的快速发展。然而,科研人员和相关企业并未退缩,积极探索并提出了一系列切实可行的解...
国产化替代产品目录   28  
热门文章
项目管理软件有哪些?
云禅道AD
禅道项目管理软件

云端的项目管理软件

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

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

内置subversion和git源码管理

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

免费试用