The description of the shell
parameter is a little hidden in the docs, and even then it presupposes some knowledge in order to know what exactly it does.
The short version is that it's a mode flag , and in shell=True
mode it expects the arguments to be a single string, like so:
# 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)
And in shell=False
mode, it expects the command as a list of strings, which it will turn internally into a proper call. This is usually preferable, since parsing a shell command correctly can end up being really really tricky:
# 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)
There is a whole module dedicated to shell lexing in the standard library called shlex
, and by using shell=False
mode you'll never have to mess with it, which is nice.
Your example is in so far a special case as the command only contains a single argument, in which case both modes are just a little more permissive and pretend that they can handle either form of input - a single string or a list of strings.
But as soon as you have two arguments, their behavior differs:
# 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'