What are the advantages of using list over string in subprocess methods? The ones I understand so far:
- Security if input comes from external sources
- Portability over different operating systems
Are there any others?
In my particular case, I'm using subprocess library to run tests on a software. Input does not come from external source. Tests are run only on Linux. Therefore, I see no benefit of lists over strings.
On POSIX, list and string arguments have different meaning and are used in different contexts.
You use a string argument and shell=True
to run a shell command e.g.:
from subprocess import check_output
output = check_output("dmesg | grep hda", shell=True)
A list argument is used to run a command without the shell e.g.:
from subprocess import check_call
check_call(["ls", "-l"])
One exception is that call("ls")
is equivalent to call(["ls"])
(a command with no arguments).
You should use a list argument with shell=False
(default) except in those cases when you need the shell so the string argument is used.
It is almost always an error to use a list argument and shell=True
(the arguments are interpreted as arguments to the shell itself instead of the command in this case). Don't use it.
If your question: what are the advantages of shell=False
and hence the list argument over a string argument:
- you don't need to escape the arguments, no shell interpolation such as word splitting, parameter expansion, command substitution occurs: what you see is what you get
- support for arguments with spaces
- support for arguments with special characters such as quotes, dollar sign, etc
- it is clear where arguments boundaries are. They are explicitely separated.
- it is clear what program is executed: it is the first item in the list
- an argument that is populated from an untrusted source won't be able to execute arbitrary commands
- why run a superfluous shell process unless you need it
Sometimes, it might be more convenient/readable to specify an argument as a string in the source code; shlex.split()
could be used to convert it to a list:
import shlex
from subprocess import check_call
cmd = shlex.split('/bin/vikings -input eggs.txt -output "spam spam.txt" '
'''-cmd "echo '$MONEY'"''')
check_call(cmd)
See the docs.
On Windows, the arguments are interpreted differently. The native format is a string and the passed list is converted to a string using subprocess.list2cmdline()
function that may not work for all Windows programs. shell=True
is only necessary to run builtin shell commands.
If list2cmdline()
creates a correct command line for your executable (different programs may use different rules for interpreting the command line) then a list argument could be used for portability and to avoid escaping separate arguments manually.