17

I am using Python script to invoke a Java virtual machine. The following command works:

subprocess.call(["./rvm"], shell=False)  # works
subprocess.call(["./rvm xyz"], shell=True) # works

But,

subprocess.call(["./rvm xyz"], shell=False) # not working

does not work. Python documentation advices to avoid shell=True.

dev
  • 2,474
  • 7
  • 29
  • 47

4 Answers4

36

You need to split the commands into separate strings:

subprocess.call(["./rvm", "xyz"], shell=False)

A string will work when shell=True but you need a list of args when shell=False

The shlex module is useful more so for more complicated commands and dealing with input but good to learn about:

import shlex

cmd = "python  foo.py"
subprocess.call(shlex.split(cmd), shell=False)

shlex tut

Padraic Cunningham
  • 176,452
  • 29
  • 245
  • 321
  • This answer also solved my problem. Can you elaborate on why "A string will work when shell=True but you need a list of args when shell=False" is? I've heard `shell=True` is extremely dangerous when there is input so I'm trying to avoid it. – Tommy Aug 26 '15 at 19:11
  • 2
    @Tommy, generally shell=True is best avoided, it is not the end of the world if you use it if you are completely in control of what you are running but almost everything that you can do with shell=True you can do with shell=False. When you pass a list of args at least on unix the first arg is the command to execute and the rest are the args for that command, if you use shell=True you must pass the command exactly as you would if you were passing it to the shell, example of what shell=True could allow https://docs.python.org/2/library/subprocess.html#frequently-used-arguments – Padraic Cunningham Aug 26 '15 at 19:32
  • 1
    i think it is a strange API to have the structure of one argument change (here the arg list or command) based on another boolean argument (whether Shell=True). This should be internally handled in subprocess so that the same argument is passed regardless of `shell`. – Tommy Aug 27 '15 at 13:38
8

If you want to use shell=True, this is legit, otherwise it would have been removed from the standard library. The documentation doesn't say to avoid it, it says:

Executing shell commands that incorporate unsanitized input from an untrusted source makes a program vulnerable to shell injection, a serious security flaw which can result in arbitrary command execution. For this reason, the use of shell=True is strongly discouraged in cases where the command string is constructed from external input.

But in your case you are not constructing the command from user input, your command is constant, so your code doesn't present the shell injection issue. You are in control of what the shell will execute, and if your code is not malicious per se, you are safe.

Example of shell injection

To explain why the shell injection is so bad, this is the example used in the documentation:

>>> from subprocess import call
>>> filename = input("What file would you like to display?\n")
What file would you like to display?
non_existent; rm -rf / #
>>> call("cat " + filename, shell=True) # Uh-oh. This will end badly...

Edit

With the additional information you have provided editing the question, stick to Padraic's answer. You should use shell=True only when necessary.

Community
  • 1
  • 1
enrico.bacis
  • 30,497
  • 10
  • 86
  • 115
4

In addition to Enrico.bacis' answer, there are two ways to call programs. With shell=True, give it a full command string. With shell=False, give it a list.

If you do shell tricks like *.jpg or 2> /dev/null, use shell=True; but in general I suggest shell=False -- it's more durable as Enrico said.

source

import subprocess
subprocess.check_call(['/bin/echo', 'beer'], shell=False)
subprocess.check_call('/bin/echo beer', shell=True)

output

beer
beer
johntellsall
  • 14,394
  • 4
  • 46
  • 40
0

Instead of using the filename directory, add the word python in front of it, provided that you've added python path to your environmental variables. If you're not sure, you can always rerun the python installer, once again, provided that you have a new version of python.

Here's what I mean:

import subprocess
subprocess.Popen('python "C:/Path/To/File/Here.py"')
Ramesh-X
  • 4,853
  • 6
  • 46
  • 67