152

Since os.popen is being replaced by subprocess.popen, I was wondering how would I convert

os.popen('swfdump /tmp/filename.swf/ -d')

to subprocess.popen()

I tried:

subprocess.Popen("swfdump /tmp/filename.swf -d")
subprocess.Popen("swfdump %s -d" % (filename))  # NOTE: filename is a variable
                                                # containing /tmp/filename.swf

But I guess I'm not properly writing this out. Any help would be appreciated. Thanks

Neuron
  • 5,141
  • 5
  • 38
  • 59
Stupid.Fat.Cat
  • 10,755
  • 23
  • 83
  • 144

4 Answers4

205

subprocess.Popen takes a list of arguments:

from subprocess import Popen, PIPE

process = Popen(['swfdump', '/tmp/filename.swf', '-d'], stdout=PIPE, stderr=PIPE)
stdout, stderr = process.communicate()

There's even a section of the documentation devoted to helping users migrate from os.popen to subprocess.

Blender
  • 289,723
  • 53
  • 439
  • 496
  • 21
    @HansThen `shell=True` is **not** recommended. – Pierre GM Sep 26 '12 at 15:50
  • Only when combined with __untrusted__ input. In this case you can use it just fine. – Hans Then Sep 26 '12 at 15:53
  • 1
    why is `shell=True` not recommended? – Alex Sep 26 '12 at 16:23
  • @HansThen "Just fine"? How do you know `filename` is not derived from user input? – Lukas Graf Sep 26 '12 at 16:40
  • 9
    @Lukas Graf Since it says so in the code. @Alex shell=True is considered a security risk when used to process untrusted data. A clever attacker can modify the input to access arbitrary system commands. E.g. by inputting `filename.swf; rm -rf /` for the value of filename. However, this is only a problem, when the contents of your argument to Popen is insecure. – Hans Then Sep 26 '12 at 16:44
  • @HansThen: It just says what an example value of `filename` is, or am I missing something? – Lukas Graf Sep 26 '12 at 16:46
  • 12
    @Lukas Graf From the code fragment I strongly doubt that was meant as an example value, to be filled by untrusted user supplied data. But I am prepared to call a truce on that item. My point was more that there is no reason not to use `shell=True` except when using __untrusted__ input. Simply stating that `shell=True` is not recommended is misleading. – Hans Then Sep 26 '12 at 16:52
  • @HansThen: Well, and I strongly doubt the OP has `/tmp/filename.swf` hardcoded in his application, but instead ends up with this string by doing path manipulation etc.., _possibly_ on user input. But besides that, `shell=True` has other disadvantages: You spawn an unnecessary shell process that simply isn't needed in this case, and quoting and escaping can get really tricky. – Lukas Graf Sep 26 '12 at 16:57
  • 7
    @HansThen: P.S. Don't get me wrong, I'm not trying to get on your case here. It's just that you seem to be aware of the risks involved with `shell=True`, but any random user stumbling across this question might not be. That's why I think it's important to highlight that `shell=True` in fact isn't recommended, unless you know exactly what you're doing. – Lukas Graf Sep 26 '12 at 17:02
  • @LukasGraf, @HansThen: If `shell=True` was so harmful, it wouldn't be included in `subprocess.Popen()` as a keyword argument. There are security risks involved in executing untrusted user input and the benefit of using a `list` to hold the components of the command is that it escapes and properly quotes the arguments, which may include unwanted characters. `shell=True` can be used if you're literally translating Bash commands to Python (or, correct me if I'm wrong, piping command output as well). – Blender Sep 26 '12 at 17:54
  • 5
    @Blender Nobody said it was harmful - it's merely dangerous. But aside from that, your argument doesn't make sense at all. Many OS functions that the Python standard library exposes are potentially dangerous - take `shutil.rmtree` for example. But that has nothing to do with whether they are included in the stdlib or not. I believe the UNIX philosophy of _"Unix was not designed to stop its users from doing stupid things, as that would also stop them from doing clever things."_ also applies in large parts to Python. – Lukas Graf Sep 26 '12 at 18:01
  • 1
    @LukasGraf: I don't have an argument. I'm merely saying that using `shell=True` is discouraged when including arbitrary input. The documentation says: *For this reason, the use of `shell=True` is strongly discouraged in cases where the command string is constructed from external input*. Why give the command more chances to fail when you can give it less? – Blender Sep 26 '12 at 19:18
  • @Blender: I'm sorry, I don't get what you mean by your last sentence. As for your argument, I was referring to _"If shell=True was so harmful, it wouldn't be included"_. But either way, I think we all agree on the technical facts anyway ;) – Lukas Graf Sep 26 '12 at 19:31
  • shouldn't you be using `process.communicate()` rather than `process.stdout` to ensure that stderr is handled properly? – Jason S May 26 '13 at 00:58
  • Can I split the full command (for example "cmd -x -y 777 -z home/blah") by space to create a valid list of inputs to subprocess.pOpen() ? – Omroth Sep 03 '19 at 14:47
16

In the recent Python version, subprocess has a big change. It offers a brand-new class Popen to handle os.popen1|2|3|4.

The new subprocess.Popen()

import subprocess
subprocess.Popen('ls -la', shell=True)

Its arguments:

subprocess.Popen(args, 
                bufsize=0, 
                executable=None, 
                stdin=None, stdout=None, stderr=None, 
                preexec_fn=None, close_fds=False, 
                shell=False, 
                cwd=None, env=None, 
                universal_newlines=False, 
                startupinfo=None, 
                creationflags=0)

Simply put, the new Popen includes all the features which were split into 4 separate old popen.

The old popen:

Method  Arguments
popen   stdout
popen2  stdin, stdout
popen3  stdin, stdout, stderr
popen4  stdin, stdout and stderr

You could get more information in Stack Abuse - Robert Robinson. Thank him for his devotion.

Mai Hai
  • 976
  • 8
  • 12
12

Use sh, it'll make things a lot easier:

import sh
print sh.swfdump("/tmp/filename.swf", "-d")
amoffat
  • 696
  • 4
  • 12
  • 2
    sh, it's good, however it's not same with Popen of subprocess. sh is alike with call of subprocess. – liuyang1 Jul 20 '15 at 12:16
11

It may not be obvious how to break a shell command into a sequence of arguments, especially in complex cases. shlex.split() can do the correct tokenization for args (I'm using Blender's example of the call):

import shlex
from subprocess import Popen, PIPE
command = shlex.split('swfdump /tmp/filename.swf/ -d')
process = Popen(command, stdout=PIPE, stderr=PIPE)
stdout, stderr = process.communicate()

https://docs.python.org/3/library/subprocess.html

SherylHohman
  • 16,580
  • 17
  • 88
  • 94
efimp
  • 209
  • 3
  • 2