shell 参数在文档中的描述是a little hidden,即使这样,它也需要一些知识才能知道它到底做了什么。
简短的版本是它是一个模式标志,在shell=True 模式下,它期望参数是单个字符串,如下所示:
# I'm using subprocess.run instead of subprocess.call, they are very similar in
# what they do but subprocess.run has a nicer interface
from subprocess import run
>>> run('ls -l', shell=True)
total 0
-rw-r--r-- 1 root root 0 Aug 26 16:36 file_a.txt
-rw-r--r-- 1 root root 0 Aug 26 16:36 file_b.txt
CompletedProcess(args='ls -l', returncode=0)
在shell=False 模式下,它期望命令作为字符串列表,它会在内部将其转换为正确的调用。这通常是可取的,因为正确解析 shell 命令最终会非常棘手:
# this is the equivalent shell=False command
>>> run(['ls', '-l'], shell=False)
total 0
-rw-r--r-- 1 root root 0 Aug 26 16:36 file_a.txt
-rw-r--r-- 1 root root 0 Aug 26 16:36 file_b.txt
CompletedProcess(args=['ls', '-l'], returncode=0)
在标准库中有一个专门用于 shell 词法分析的完整模块,称为 shlex,通过使用 shell=False 模式,您将永远不必弄乱它,这很好。
到目前为止,您的示例是一种特殊情况,因为该命令仅包含一个参数,在这种情况下,两种模式都稍微宽松一点,并假装它们可以处理任何一种形式的输入-单个字符串或列表字符串。
但是一旦你有两个参数,它们的行为就会不同:
# shell=True with an argument list, only runs the "ls" part, not "ls -l"
>>> run(['ls', '-l'], shell=True)
file_a.txt file_b.txt
CompletedProcess(args=['ls', '-l'], returncode=0)
# and shell=False with two arguments as a non-list fares even worse
>>> run('ls -l', shell=False)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "/usr/lib/python3.6/subprocess.py", line 423, in run
with Popen(*popenargs, **kwargs) as process:
File "/usr/lib/python3.6/subprocess.py", line 729, in __init__
restore_signals, start_new_session)
File "/usr/lib/python3.6/subprocess.py", line 1364, in _execute_child
raise child_exception_type(errno_num, err_msg, err_filename)
FileNotFoundError: [Errno 2] No such file or directory: 'ls -l': 'ls -l'