Saturday, September 26, 2009

Python, Subprocess and Multiple Arguments

Calling external programs from within a python script is a pretty common task. However, most of the times when I want to pass some arguments to the external program, I usually get stuck. The official Python documentation for the subprocess module is a bit lacking, in my humble opinion, in its treatment of argument passing.

This is not to say that all the information isn't available in the official documentation. In fact, it is. It's just not where I always look for it. The first thing I look for are examples. I simply do not have time to read the entire documentation page for something which should not be too difficult.

The basic idea is this:
  1. Either provide a string of the entire command and argument, but use Shell=True
  2. Provide a string for only the command
  3. Provide a list of strings, where the first item is the command and the other items are the arguments
It's the third option that keeps on giving me trouble, so here is a simple working example for reference:
 1 #!/bin/env python
 2 import subprocess
 3 
 4 program_name = "ls"
 5 arguments = ["-l", "-a"]
 6 
 7 command = [program_name]
 8 command.extend(arguments)
 9 
10 output = subprocess.Popen(command, stdout=subprocess.PIPE).communicate()[0]
11 print output


Passing Arguments with Quotes
If you want to pass arguments that are passed with quotes in the shell, then just pass them as a single list item, without the quotes.

Links
  1. James Gardner wrote the most comprehensive introduction to the python subprocess module I've encountered.
  2. The official documentation.

Further Reading
  1. Bash: Passing Arguments with Quotes
  2. Why Not to Write Shell Scripts

6 comments:

  1. I got stuck on argument passing with subprocess as well, but this helped. Thanks!

    ReplyDelete
  2. This helped me out. Thanks!

    ReplyDelete
  3. This is great. Thank you! I beat my head over trying to get this to work, for hours, and used the string way --- until it broke. Coming back here, I finally got the 'right' way working.

    One thing to watch out for (in my case, I was doing pdftk) is that call commands MUST be broken apart. The 'cat output' in pdftk was killing me; it has to be 'cat', 'output'. This is obvious in hindsight, but it's the simple stuff... ;-)

    ReplyDelete
  4. It's great you took the time to dig through the documentation and share it, it saved me some time and frustration, thanks!

    ReplyDelete
  5. Command can also be composed in 1 line:

    command = ["openssl", "x509", "-in", sys.argv[1], "-text", "-noout"]

    ReplyDelete