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

use*_*312 236 python subprocess

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

在以下代码中:

callProcess = subprocess.Popen(['ls', '-l'], shell=True)
Run Code Online (Sandbox Code Playgroud)

callProcess = subprocess.Popen(['ls', '-l']) # without shell
Run Code Online (Sandbox Code Playgroud)

两者都有效.阅读文档后,我开始知道这shell=True意味着通过shell执行代码.这意味着在缺席的情况下,该过程将直接启动.

那么我应该更喜欢我的情况 - 我需要运行一个进程并获得其输出.从shell内部或外部调用它有什么好处.

Hea*_*utt 164

不通过shell调用的好处是你没有调用'神秘程序'.在POSIX上,环境变量SHELL控制将哪个二进制文件作为"shell"调用.在Windows上,没有bourne shell后代,只有cmd.exe.

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

通过shell调用允许您根据shell的常用机制扩展环境变量和文件globs.在POSIX系统上,shell将文件globs扩展为文件列表.在Windows中,一个文件名匹配(例如,"*.*")不受外壳,反正扩大(但在命令行环境变量由cmd.exe的展开).

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

总之,使用shell=False.

  • 如果你在开始时不小心,那么无后顾之忧就会帮助你赶上.;) (47认同)
  • 关于`$ SHELL`的陈述是不正确的.引用subprocess.html:"在Unix上使用`shell = True`,shell默认为`/ bin/sh`." (不是`$ SHELL`) (8认同)
  • 谢谢你的回答.虽然我真的不在那个我应该担心漏洞的阶段,但我明白你的目标是什么. (2认同)
  • 如果不涉及用户输入,并且脚本不适合移植,那么 `shell=True` 应该没问题,对吧? (2认同)

Min*_*iel 107

>>> 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
Run Code Online (Sandbox Code Playgroud)

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

source:子进程模块

  • 不知道为什么这不是选定的答案.到目前为止,实际上匹配问题的那个 (13认同)
  • 这是我读过的'shell = True`最清晰的陈述. (5认同)
  • 同意。这是我理解 shell=True 含义的一个很好的例子。 (3认同)
  • *将shell参数设置为true会导致子进程产生一个中间shell进程,并告诉它运行命令。为什么不接受此答案???为什么? (2认同)

lun*_*orn 39

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

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

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


Ric*_*eek 35

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

>>> from subprocess import call
>>> filename = input("What file would you like to display?\n")
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...
Run Code Online (Sandbox Code Playgroud)

检查doc:subprocess.call()

  • 该链接非常有用.正如链接所述:_Excing shell命令包含来自不受信任来源的未经过处理的输入,这使得程序易受shell注入攻击,这是一个严重的安全漏洞,可能导致任意命令执行.因此,在从外部输入构造命令字符串的情况下,强烈建议不要使用shell = True. (5认同)
  • 请注意,即使“shell=False”,您仍然必须小心。例如,如果“filename”是“-r”,或者是像“../../private”这样的路径,则“call(["rm", filename1, filename2])”可能会出现意外行为/路径/文件名.txt` 。使用双破折号并确保文件名不是您不期望的路径。 (4认同)

tri*_*eee 15

这里的其他答案充分解释了subprocess文档中也提到的安全警告.但除此之外,启动shell以启动要运行的程序的开销通常是不必要的,对于实际上没有使用任何shell功能的情况来说,这绝对是愚蠢的.此外,额外隐藏的复杂性应该吓到你,特别是如果你不熟悉shell或它提供的服务.

通配符扩展,变量插值和重定向都很容易用本机Python结构替换.一个复杂的shell管道,部分或全部不能在Python中合理地重写(专用外部工具,也许是封闭的源?)将是你可以考虑使用shell的一种情况.你应该对此感到难过.

在简单的情况下,只需更换

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

subprocess.Popen(['command', '-with','-options', 'like this', 'and an argument'])
Run Code Online (Sandbox Code Playgroud)

请注意第一个参数是如何传递给的字符串列表shell=True,以及引用字符串和反斜杠转义shell元字符的方式通常不是必需的(或者是有用的,或者是正确的).

execvp()顺便说一句,如果Popen包中的一个更简单的包装器做了你想要的东西,你经常要避免.如果你有一个最近足够的Python,你应该使用subprocess.

  • 随着subprocess.run如果命令你运行失败就会失败.
  • check=True它来捕获命令的输出.
  • 有点模糊,用stdout=subprocess.PIPE它将输出解码为一个合适的Unicode字符串(universal_newlines=True在Python 3上它只是在系统编码中).

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

我将结束David Korn的一句话:"编写便携式shell比使用便携式shell脚本更容易." 甚至check_call不能移植到Windows.