1

After writing a very simple python script to ls the contents of my project directory as a test:

from subprocess import *
p = Popen(['ls /Users/Nelson/Projects'], stdout=PIPE, shell=True)
print(p.communicate()[0].decode())

It kicked out the error:

/bin/bash: ls: command not found

I did a bit of testing and discovered the basic commands pwd, echo, and cd all word perfectly fine, but not ls. I tried using the option executable='/bin/bash' because it is defaulting to /bin/sh, but that did nothing.

This script has been tested on Ubuntu 14.04 LTS and Ubuntu 16.04 LTS, both work fine.

I am using Mac OSX 10.12.3 Sierra if that means anything in this situation

Any responses are greatly appreciated!

Nelson
  • 922
  • 1
  • 9
  • 23
  • For the record, `pwd`, `echo` and `cd` are all `bash` builtins, while `ls` is a separate executable program, not a builtin. That may be the source of the behavior discrepancy. – ShadowRanger Feb 17 '17 at 03:30

1 Answers1

4

If you use list invocation to run a simple command, split the arguments yourself (and skip shell=True). Try:

Popen(['ls', '/Users/Nelson/Projects'], ... leave off shell=True ...)

which splits the command (ls) from the sole argument. If you have more than one argument, they must also be separated from each other.

If you insist on shell=True (a terrible idea most of the time), don't use a list, just the raw string:

Popen('ls /Users/Nelson/Projects', ...)

Technically, there are use cases for list of commands plus shell=True; your command could work as Popen(['ls "$0"', '/Users/Nelson/Projects'], stdout=PIPE, shell=True), where the first element of the list is run as a shell command, and subsequent elements form the $0, $1, etc. arguments to it (and it's entirely non-portable in this case; I'm not sure it works at all on Windows, and even if it does, the scripting language used is completely different). I'd strongly discourage it (you're writing Python, and adding a layer of shell scripting between it and the command you're running is just getting uglier and uglier) when possible, as in this case, where my first suggestion is simpler, safer and faster.

ShadowRanger
  • 143,180
  • 12
  • 188
  • 271
  • Thank you so much! I had tried with and without inputting the command as 1 string or 2, and with and without shell=True. I guess I just missed that combination – Nelson Feb 17 '17 at 05:25
  • This is untrue. You absolutely can pass a list to `shell=True`. The first element of that list is parsed as a shell script; subsequent arguments are `$0`, `$1`, etc. in that shell. – Charles Duffy Sep 20 '17 at 13:55
  • @CharlesDuffy: Missed this way back when, but you're right. It's an unusual use case, but yes, technically valid. I'll remove that note. – ShadowRanger Mar 26 '21 at 02:39