subprocess中'shell=True'的实际意义

421 投票
7 回答
350862 浏览
提问于 2025-04-16 00:46

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

在下面的代码中:

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

还有

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

这两种方式都能正常工作。看完文档后,我了解到 shell=True 的意思是通过命令行来执行代码。这意味着如果不加这个选项,进程就是直接启动的。

那么在我的情况下,我应该选择哪种方式呢?我需要运行一个进程并获取它的输出。通过命令行调用和直接调用有什么好处呢?

7 个回答

65

这里有一个例子,说明使用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()

232
>>> 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参数设置为一个真实的值,会让子进程启动一个中间的shell进程,并告诉它去执行这个命令。换句话说,使用中间的shell意味着在执行命令之前,命令字符串里的变量、通配符和其他特殊的shell功能都会被处理。在这个例子中,$HOME在执行echo命令之前就已经被处理了。实际上,这就是命令有shell扩展的情况,而命令ls -l则被认为是一个简单的命令。

来源:Subprocess Module

296

不通过命令行调用的好处是,你不会启动一个“神秘程序”。在POSIX系统中,环境变量SHELL决定了哪个程序被当作“命令行”来运行。在Windows系统中,没有类似的程序,只有cmd.exe。

所以,通过命令行调用会启动用户选择的程序,并且这个选择依赖于操作系统。一般来说,最好避免通过命令行来调用程序。

不过,通过命令行调用可以让你使用环境变量和文件通配符,这些都是命令行的常规功能。在POSIX系统中,命令行会把文件通配符扩展成文件列表。在Windows中,文件通配符(比如“*.*”)不会被命令行扩展,但在命令行中,环境变量是会被cmd.exe扩展的。

如果你觉得需要环境变量扩展和文件通配符,建议你了解一下1992年左右的ILS攻击,这些攻击是针对通过命令行调用子程序的网络服务的。比如,涉及ILS的各种sendmail后门。

总之,建议使用shell=False

撰写回答