Alex
Alex

Reputation: 44385

How to run `ls` command with 'subprocess.check_call' containing a placeholder?

In python (3.6.8) you can use subprocess.check_call to run a command, for example

import subprocess
subprocess.check_call("ls -l /home/user".split())

which, in the example, prints out the content of the home directory:

...
drwx------ 25 user user       4096 Jan 27 16:31 Music
-rw-r--r--  1 user user          0 Jul  9 11:06 nse.file.zip
-rw-rw-r--  1 user user 1938520956 Jul  8 16:59 nse.file.zip.1
-rw-------  1 user user  514916418 Jul  9 11:15 nse.file.zip.part
drwxr-xr-x  5 user user      12288 Mar 11 14:32 Pictures
drwxr-xr-x  8 user user       4096 Mar  9 06:36 Private
drwxr-xr-x  2 user user       4096 Jul 30  2018 Public
...

But, in contrast, when you want to use placeholders to list several files at once, this does not seem to work anymore:

subprocess.check_call("ls -l /home/user/nse*".split())

or

subprocess.check_call("ls -l '/home/user/nse*'".split())

In both cases I get an error

CalledProcessError: Command '['ls', '-l', '/home/user/nse*']' returned non-zero exit status 2.

or

CalledProcessError: Command '['ls', '-l', "'/home/user/nse*'"]' returned non-zero exit status 2.

What am I missing? How to fix this issue?

Upvotes: 1

Views: 460

Answers (2)

avysk
avysk

Reputation: 2035

You are missing the fact that wildcard expansion (understanding what * means) is not done by ls but it is done by shell. When you run something like ls a*.b in shell, it's shell who finds out what are a*.b files, and then passes them to ls. Now you're asking ls: give me the file named nse* literally. There's no such file, of course.

You can use subprocess.check_call(..., shell=True) to run the command through shell (which will do wildcard expansion before calling the command).

See:

>>> subprocess.check_call("ls /tmp/foo*".split())
ls: cannot access '/tmp/foo*': No such file or directory
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/usr/lib/python3.7/subprocess.py", line 347, in check_call
    raise CalledProcessError(retcode, cmd)
subprocess.CalledProcessError: Command '['ls', '/tmp/foo*']' returned non-zero exit status 2.
>>> subprocess.check_call("ls /tmp/foo*", shell=True)
/tmp/foo.bar  /tmp/foo.c
0

Notice no .split() in the second case -- the whole string is given as an argument to a shell.

Upvotes: 2

Chris_Rands
Chris_Rands

Reputation: 41188

You can do:

subprocess.check_call("ls -l /home/user/nse*", shell=True)

Upvotes: 2

Related Questions