Advantage of list over string in subprocess method

2019-09-01 10:29发布

问题:

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.

回答1:

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.