代码之家  ›  专栏  ›  技术社区  ›  user225312

子流程中“shell=True”的实际含义

  •  192
  • user225312  · 技术社区  · 15 年前

    subprocess 模块。不过,我有个问题。

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

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

    shell=True 意味着通过shell执行代码。所以这意味着在缺席的情况下,这个过程是直接开始的。

    所以,对于我的情况,我应该选择什么呢?我需要运行一个进程并获取它的输出。从shell内部或外部调用它有什么好处。

    5 回复  |  直到 12 年前
        1
  •  216
  •   Heath Hunnicutt    15 年前

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

    因此,调用shell将调用用户选择的程序,并且依赖于平台。一般来说,避免通过shell调用。

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

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

    shell=False .

        2
  •  176
  •   Mina Gabriel    9 年前
    >>> 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
    

    资料来源: Subprocess Module

        3
  •  49
  •   Richeek    10 年前

    这里显示了一个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...
    

    subprocess.call()

        4
  •  41
  •   nbro kai    10 年前

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

    shell=True os.path.expandvars() 用于参数扩展或 shlex

    外壳=真 尽一切办法。

        5
  •  22
  •   tripleee    5 年前

    这里的其他答案充分解释了安全注意事项,这些注意事项在 subprocess 尤其地 如果您不太熟悉shell或它提供的服务。

    “外显优于内隐”;

    通配符扩展、变量插值和重定向都很容易替换为本机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元字符通常是不必要的(或有用的,或正确的)。 When to wrap quotes around a shell variable?

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

    作为旁白,你经常想避免 Popen 如果 子流程 你想要什么就做什么。如果您最近有足够的Python,您可能应该使用 subprocess.run .

    • check=True 如果您运行的命令失败,它将失败。
    • stdout=subprocess.PIPE 它将捕获命令的输出。
    • text=True (或者有点含糊,用同义词) universal_newlines=True bytes

    如果不是,对于许多任务,您需要 check_output 获取命令的输出,同时检查命令是否成功,或 check_call

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

        6
  •  -1
  •   lauc.exon.nod    4 年前

    假设您正在使用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,并且不接受任何用户输入。

    所以在我的用例中,我看不到安全风险。而且创建长的命令串要容易得多。希望我没有错。