nonlinearmind
nonlinearmind

Reputation: 151

Why is my variable not be included in my subprocess.Popen?

I'm simply trying to pass along a variable to my shell script, but it isn't being handed off. I've following examples from the python docs, but it's not working. What am I missing?

subprocess.Popen(['./script.sh' + variable] , shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)

Upvotes: 0

Views: 542

Answers (4)

Charles Duffy
Charles Duffy

Reputation: 295639

You shouldn't be using shell=True here at all, unless you want any actual shell syntax in your variable (like >file.log) to be executed.

subprocess.Popen(['./script.sh', variable],
    stdout=subprocess.PIPE, stderr=subprocess.PIPE)

If you really want shell=True, you have a few options to do so securely. The first is to use pipes.quote() (or, in Python 3, shlex.quote()) to prevent shell escapes:

subprocess.Popen('./script.sh ' + pipes.quote(variable), shell=True,
    stdout=subprocess.PIPE, stderr=subprocess.PIPE)

The second is to pass the name as a subsequent argument (note the empty string, which becomes $0 in the generated shell):

subprocess.Popen(['./script.sh "$1"', '', variable], shell=True,
    stdout=subprocess.PIPE, stderr=subprocess.PIPE)

Remember, Bobby Tables isn't just for SQL -- his younger sister Susan $(rm -rf /) is out there too.

Upvotes: 2

abarnert
abarnert

Reputation: 365915

You're combining two different ways to doing things. And, on top of that, you're doing it wrong, but just fixing the "doing it wrong" isn't the answer.

You can put your two arguments in a list, and then launch it without the shell, like ['./script.sh', variable]. This is usually better. Using the shell means you have to deal with quoting, and with accidental or malicious injection, and can interfere with your input and output, and adds a performance cost. So, if you don't need it, don't use it.

Or you can put your two arguments in a string, and then launch it with the shell, like './script.sh ' + variable.

But you can't put your two arguments in a string, and then put that string in a list. In some cases, it will happen to work, but that's not something you can rely on.

In some cases, you can use a list with the shell,* or a string without the shell,** but generally you shouldn't do that unless you know what you're doing, and in any case, you still shouldn't be using a list of one string unless there's a specific reason you need to.***


If you want to use a list of arguments, do this:

subprocess.Popen(['./script.sh', variable], shell=False, …)

Notice that this is a list of two strings, not a list of one joined-up string, and that shell=False.


If you want to use a shell command line, don't put the command line in a list, don't skip the space between the arguments, and quote any non-static arguments, like this:

subprocess.Popen('./script.sh ' + shlex.quote(variable), shell=True, …)

* Using a list with the shell on Windows is never useful; they just get combined up in some unspecified way. But on Unix, subprocess will effectively prepend '/bin/sh' and '-c' to your list, and use that as the arg list for /bin/sh, which can be simpler than trying to quote shell arguments, and at least arguably more concise than explicitly calling /bin/sh with shell=False.

** Using a string without the shell on Unix is never useful; that just tries to find a program whose name is the whole string, which is going to fail (unless you're really unlucky). But on Windows, it can be useful; subprocess tries to combine your arguments into a string to be passed to CreateProcess in such a way that MSVCRT will parse them back to the same list of arguments on the other side, and in some edge cases it's necessary to create that string yourself.

*** Basically, you want to spawn ['/bin/sh', '-c', <command line>] exactly.

Upvotes: 1

reagle
reagle

Reputation: 1

Would just add a space after script name:

subprocess.Popen(['./script.sh ' + variable], shell=True,
    stdout=subprocess.PIPE, stderr=subprocess.STDOUT)

Upvotes: 0

Misha
Misha

Reputation: 136

Add space after ./script.sh:

subprocess.Popen(['./script.sh ' + variable] , shell=True, stdout=subprocess.PIPE,  stderr=subprocess.STDOUT)

Upvotes: 0

Related Questions