How does subprocess.call () work with shell = False?
I am using a Python module subprocess
to call some Linux command line functions. The documentation explains the argument shell=True
as
If a shell
True
, the specified command will be executed through the shell
There are two examples that seem the same to me from a descriptive point of view (i.e. they both invoke some command line command), but one is using shell=True
and the other is not
>>> subprocess.call(["ls", "-l"])
0
>>> subprocess.call("exit 1", shell=True)
1
My question is:
- What does the command
shell=False
do with do, as opposed toshell=True
? - I got the impression that
subprocess.call
andcheck_call
andcheck_output
all must fulfill the argument through the shell. In other words, how can he not execute the argument through the shell?
It would also be helpful to get some examples:
- Things that can be done with
shell=True
, that cannot be done with,shell=False
and why they cannot be done. - And vice versa (although it seems that there are no such examples)
- Things that don't matter,
shell=True
orFalse
why it doesn't matter.
source to share
UNIX programs run against each other with the following three calls or derivatives / equivalents:
-
fork()
- Create a new copy of yourself. -
exec()
- Replace yourself with another program (do this if you copy!). -
wait()
- Wait for another process to finish (optional if not running in the background).
So with help shell=False
you do exactly that (as the Python syntax pseudocode below - exclude wait()
if not call blocking like subprocess.call()
):
pid = fork()
if pid == 0: # we're the child process, not the parent
execlp("ls", "ls", "-l", NUL);
else:
retval = wait(pid) # we're the parent; wait for the child to exit & get its exit status
whereas shell=True
you do the following:
pid = fork()
if pid == 0:
execlp("sh", "sh", "-c", "ls -l", NUL);
else:
retval = wait(pid)
Note that with the shell=False
command we ran was ls
, whereas with the shell=True
command we ran was sh
.
I.e:
subprocess.Popen(foo, shell=True)
exactly the same as:
subprocess.Popen(
["sh", "-c"] + ([foo] if isinstance(foo, basestring) else foo),
shell=False)
That is, you do a copy /bin/sh
and pipe that copy /bin/sh
to parse the string into the argument list and do it yourself ls -l
.
So why did you use shell=True
?
-
You are calling the inline wrapper.
For example, the command is
exit
actually part of the shell itself, not an external command. However, this is a fairly small set of commands and is rarely useful in the context of a shell instance that only exists for the duration of a single callsubprocess.call()
. -
You have code with wrapper constructs (i.e. redirects) that you can emulate without any problem.
If, for example, your command
cat one two >three
, the syntax>three
is a redirect: it is not an argument forcat
, but an instruction for the shell to setstdout=open('three', 'w')
when the command is run['cat', 'one', 'two']
. If you don't want to deal with redirects and pipelines yourself, you need a wrapper to do it.The case is somewhat more complicated
cat foo bar | baz
. To do this without the shell, you need to run both sides of the pipeline on their own:p1 = Popen(['cat', 'foo', 'bar'], stdout=PIPE), p2=Popen(['baz'], stdin=p1.stdout)
. -
You don't give a damn about security bugs.
... okay, it's a little too strong, but not strong. Use is
shell=True
dangerous. You can't do this:Popen('cat -- %s' % (filename,), shell=True)
without the shell vulnerability: if your code was ever called withfilename
containing$(rm -rf ~)
, you would have a very bad day. On the other hand, it['cat', '--', filename]
is safe with all possible filenames: the filename is purely data, not parsed as source by the shell or anything else.Safe scripts can be written in the shell, but you need to be careful. Consider the following:
filenames = ['file1', 'file2'] # these can be user-provided subprocess.Popen(['cat -- "$@" | baz', '_'] + filenames, shell=True)
This code is safe (well - as safe as letting the user read any file they ever want) because it passes your out-of-range names from your script code, but it is only safe because the string passed to the shell , is fixed and hardcoded, and the parameterized content is external variables (list
filenames
). And even then it is only "safe" to the point - a bug like Shellshock that triggers the initialization of the shell will affect it as much as anything else.
source to share
I was under the impression that subprocess.call and check_call and check_output should all execute the argument through the shell.
No, a subprocess is perfectly capable of running a program directly (via a call to the operating system). It doesn't need a shell
Things that can be done with shell = True that cannot be done with shell = False
You can use shell=False
for any command that just runs some executables, possibly with some specified arguments.
You should use shell=True
if your command uses shell functions. This includes pipelines, |
or redirects, or that contain compound assertions combined with ;
or &&
or ||
, etc.
Therefore, for a command type grep string file
you can use shell=False
. But the command of the type grep string file | xargs something
, because of the |
demands shell=True
.
Since the shell has power functions that python programmers don't always find intuitive, it is considered best to use shell=False
if you don't really need a shell function. As an example, piping is really unnecessary because it can also be done using the PIPE subprocess function.
source to share