subprocess中'shell = True'的实际含义
我正在调用与subprocess
模块不同的进程。 不过,我有一个问题。
在以下代码中:
callProcess = subprocess.Popen(['ls', '-l'], shell=True)
和
callProcess = subprocess.Popen(['ls', '-l']) # without shell
两者都有效。 阅读文档后,我才知道shell=True
意味着通过shell执行代码。 所以这意味着如果没有,这个过程是直接开始的。
那么我应该怎么样呢?我需要运行一个进程并得到它的输出。 从shell或其外部调用它有什么好处。
不通过shell调用的好处是你没有调用一个“神秘程序”。 在POSIX上,环境variablesSHELL
控制哪个二进制被调用为“shell”。 在Windows上,没有Bourne shell后代,只有cmd.exe。
所以调用shell调用用户select的程序,并且依赖于平台。 一般来说,避免通过shell进行调用。
通过shell调用确实可以让你根据shell通常的机制来扩展环境variables和文件大小。 在POSIX系统上,shell将文件扩展到文件列表。 在Windows上,文件glob(例如,“*。*”)不会被shell扩展,但是命令行上的环境variables是由cmd.exe扩展的。
如果你认为你需要环境variables扩展和文件扩展,研究1992年的ILS
攻击是通过shell执行子程序调用的networking服务。 例子包括涉及ILS
的各种sendmail
后门。
总之,使用shell=False
。
通过shell执行程序意味着所有传递给程序的用户input都是根据调用的shell的语法和语义规则来解释的。 这最多只会给用户造成不便,因为用户必须遵守这些规则。 例如,包含特殊shell字符(如引号或空格)的path必须转义。 最糟糕的是,它会导致安全漏洞,因为用户可以执行任意程序。
shell=True
有时可以方便地使用特定的shell特性,如分词或参数扩展。 但是,如果需要这样的function,则可以使用其他模块(例如,用于参数扩展的os.path.expandvars()
或用于分词的shlex
)。 这意味着更多的工作,但避免了其他问题。
总之:避免shell=True
。
这里显示了一个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()
>>> 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意味着在运行该命令之前处理命令string中的variables,全局模式和其他特殊shell特征。 在这里,在这个例子中,$ HOME是在echo命令之前处理的。 实际上,这是使用shell扩展的命令,而命令ls -l被视为一个简单的命令。
源: subprocess模块
这里的其他答案充分解释了subprocess
文档中也提到的安全性问题。 但是除此之外,启动一个shell来启动你想运行的程序的开销往往是不必要的,而且对于实际上没有使用任何shell的function的情况来说,这个开销是愚蠢的。 而且,额外的隐藏复杂性应该吓倒你, 特别是如果你不熟悉shell或者它提供的服务。
通配符扩展,variables插值和redirect都可以简单地用本地Python结构来代替。 一个复杂的shellpipe道,其中部分或全部不能用Python合理地重写(专门的外部工具,可能是封闭的源代码?)将是可能考虑使用shell的一种情况。 你应该仍然感觉不好。
在微不足道的情况下,只需更换
subprocess.Popen("command -with -options 'like this' and\\ an\\ argument", shell=True)
同
subprocess.Popen(['command', '-with','-options', 'like this', 'and an argument'])
请注意,第一个参数是如何传递给execvp()
的string列表,以及如何引用string和反斜杠转义的shell元字符通常不是必需的(或有用的或正确的)。
Popen
如果subprocess
包中的一个简单的包装做到了你想要的,你经常想要避免Popen
。 如果你有足够新的Python,你应该使用subprocess.run
。 当check=True
,如果你运行的命令失败,它将会失败。 使用stdout=subprocess.PIPE
它将捕获命令的输出(有点模糊, universal_newlines=True
将其解码为适当的Unicodestring)。
如果不是这样,对于许多任务,您希望check_output
从命令获取输出,同时检查是否成功,如果没有输出要收集,则check_call
。
大卫·科恩(David Korn)的一句名言:“编写一个可移植的shell比一个可移植的shell脚本更容易。”