8

It seems whenever I try to use Python's subprocess module, I find I still don't understand some things. Currently, I was trying to join 3 mp4 files from within a Python module.

When I tried

z ='MP4Box -cat test_0.mp4 -cat test_1.mp4 -cat test_2.mp4 -new test_012d.mp4'
subprocess.Popen(z,shell=True)

Everything worked.

When I tried

z = ['MP4Box', '-cat test_0.mp4', '-cat test_1.mp4', '-cat test_2.mp4', '-new test_012d.mp4']
subprocess.Popen(z,shell=False)

I got the following error:

Option -cat test_0.mp4 unknown. Please check usage

I thought that for shell=False I just needed to supply a list where the first element was the executable I wanted to run and each succeeding element was an argument to that executable. Am I mistaken in this belief, or is there a correct way to create the command I wanted to use?

Also, are there any rules for using Shell=True in subprocess.Popen? So far, all I really know(?) is "don't do it - you can expose your code to Shell injection attacks". Why does Shell=False avoid this problem? Is there ever an actual advantage to using 'Shell=True`?

user1245262
  • 6,968
  • 8
  • 50
  • 77
  • 2
    try with [`shlex.split`](https://docs.python.org/3.5/library/shlex.html#shlex.split) for correct tokenization of arg, as in the example in [here](https://docs.python.org/3.5/library/subprocess.html#popen-constructor) – behzad.nouri Jun 14 '15 at 14:44
  • 1
    @Ami Tavory - Thanks, I think that does answer my question. But, to be certain I understand, when `shell=True`, the shell program will parse the cmd string. Because the shell program is meant to be very general, it will accept and execute a far larger variety of commands than I intended for what is likely meant to handle only a very specific command and its options. Use of `shell=False` and shlex limits the set of strings that will be executed to something closer to my specific use-case. Is that a correct understanding? – user1245262 Jun 14 '15 at 15:03
  • @user1245262, ...basically, `shell=True` honors _all_ shell syntax, including things like command substitution; `shlex.split` honors quotes and spaces only -- not command substitutions, or command separators, or so forth. Honestly, I often think people recommend even `shlex.split` when it's not a good idea -- using it means that filenames that _contain_ quotes/spaces/etc can't be used without making trouble. Sticking with the default `shell=False` and providing an explicit list is the safest route. – Charles Duffy Dec 19 '22 at 21:06

2 Answers2

15

If shell is True, the specified command will be executed through the shell. This can be useful if you are using Python primarily for the enhanced control flow it offers over most system shells and still want convenient access to other shell features such as shell pipes, filename wildcards, environment variable expansion, and expansion of ~ to a user’s home directory.

When shell=True is dangerous?

If we execute shell commands that might include unsanitized input from an untrusted source, it will make 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

Eg. (Taken from docs)

>>> 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..
Rahul Gupta
  • 46,769
  • 10
  • 112
  • 126
4

You have to give every single argument as one element of a list:

z = ['MP4Box', '-cat', 'test_0.mp4', '-cat', 'test_1.mp4', '-cat', 'test_2.mp4', '-new', 'test_012d.mp4']
subprocess.Popen(z,shell=False)

This is normally what you want to do, because you don't need to escape especial characters of the shell in filenames.

Daniel
  • 42,087
  • 4
  • 55
  • 81