1

In most answers (I found) to the question "How to run an external command" I see something along the lines

If you want to run ls -l you need to use subprocess.call(["ls", "-l"])

What I normally do when I know what I will be running is to call subprocess.call('ls -l'.split(' ')) to have visually the command line in one piece (it is usually a variable).

Is there anything inherently wrong with using split() as opposed to building the list manually (again, when the command is known). Or are these answers crafted to explicitly show that a list is needed?

I tried to find some drawbacks (multiple spaces, escaped spaces, ...) but I do not see where that approach could go wrong?

Note: this question is specifically about the robustness of splitting on spaces, not the security issues or other (very important) considerations as such.

Community
  • 1
  • 1
WoJ
  • 27,165
  • 48
  • 180
  • 345
  • 6
    You should be using [`shlex.split`](https://docs.python.org/2/library/shlex.html#shlex.split) for this. – Ashwini Chaudhary May 05 '16 at 17:44
  • 1
    in most cases you don't have constant command arguments: `["ls", "-l", pathname]` – Daniel May 05 '16 at 17:46
  • 2
    I agree with Ashwini. `shlex.split` will handle quoting properly ... `ls -l "file with spaces"`, `str.split` won't. Additionally, 99% of the time when you write `str.split(' ')`, you _really_ want `str.split()` (which will handle consecutive runs of whitespace in a way that you _usually_ want). – mgilson May 05 '16 at 17:46
  • You may use commands.getoutput() instead – Fabiano May 05 '16 at 18:34

2 Answers2

2

Observe that this works:

>>> import subprocess
>>> subprocess.call(['ls', '-l', "my file"])
-rw-rw---- 1 john john 0 May  5 10:46 my file
0

But this does not:

>>> subprocess.call('ls -l "my file"'.split(' '))
ls: cannot access "my: No such file or directory
ls: cannot access file": No such file or directory
2

And this does work:

>>> import shlex
>>> shlex.split('ls -l "my file"')
['ls', '-l', 'my file']
>>> subprocess.call(shlex.split('ls -l "my file"'))
-rw-rw---- 1 john john 0 May  5 10:46 my file
0

Recommendation

In python philosphy, explicit is better than implicit. Thus, of those three forms, use this one:

subprocess.call(['ls', '-l', 'my file'])

This avoids all preprocessing and shows you clearly and unambiguously and explicitly what will be executed and what its arguments are.

John1024
  • 109,961
  • 14
  • 137
  • 171
0

If you are really certain that there are no issues with unescaped characters (including spaces in filenames), then I don't see any problem.

But you might find it easier to use os.system("ls -l") instead. Just note that if you want to use the result code, it should be right shifted by 8 bits:

exit_code = os.system("ls -l") >> 8

Alternatively (more robust, but also more work to type...):

subprocess.call('ls -l', shell=True)

Then you let the shell handle the splitting.

Edit: also mention subprocess.

Han-Kwang Nienhuys
  • 3,084
  • 2
  • 12
  • 31
  • 1
    [The docs](https://docs.python.org/3/library/os.html#os.system) for os.system recommend using subprocess instead. – PM 2Ring May 05 '16 at 18:25