Passing arguments which include double quotes to subprocess using Python

BetterOffEd picture BetterOffEd · Jun 6, 2013 · Viewed 9.3k times · Source

I'm looking for a way to pass an argument which contains double quotes to python's subprocess module, without the double quotes being escaped.

I've seen this question asked a few different times when the quotes are surrounding the arg, but the answer is usually to remove the quotes. The issue I've hit is that the quotes are part of the argument and must be passed to the script without prepending a back slash to each quote

example:

I'm calling subprocesTest.bat, a simple batch script that takes an argument and echos it:

echo "%~1"

I invoke the batch script from python with an arg that includes quotes:

import subprocess
test_string = 'Bill said "Hi!"'
cmd = ["subprocesTest.bat",
       test_string
       ]

p = subprocess.Popen(cmd)
p.wait()

expected output:

"Bill said "Hi!""

actual output:

"Bill said \"Hi!\""

I can get the expected output when calling the same batch script from the command line as follows:

subprocesTest.bat "Bill said "Hi!""

Looking at the source, subprocess.py's list2cmdline function is documented with:

 3) A double quotation mark preceded by a backslash is
   interpreted as a literal double quotation mark.

However, if I step through this function, I see no means of passing a double quote through without prepending a backslash:

        elif c == '"':
            # Double backslashes.
            result.append('\\' * len(bs_buf)*2)
            bs_buf = []
            result.append('\\"')

So I have two questions...

  • Is it possible to pass an arg to subprocess which contains a double quote wihtout prepending a backslash
  • If it is not possible, can someone explain to me why the function was designed this way? Item three in the funcion's doc string seems to indicate that I can pass a literal quote by prepending a backslash, but this doesn't yield the behavior I was looking for. Am I reading the doc wrong?

Here are other threads where this question is asked, and worked around by not using quotes:

I see no response for a situation where I actually want to pass quotes in my argument

Answer

7stud picture 7stud · Jun 6, 2013

1) Yes, but maybe not on windows and/or with a .bat file. For example, what happens when you run the windows equivalent of this:

import subprocess

test_string = 'Bill said "Hi!"'

cmd = ["./myprog.py",
       test_string
       ]

p = subprocess.Popen(cmd)
p.wait()

myprog.py:

#!/usr/bin/env python

import sys

print '"{}"'.format(sys.argv[1])

2) Well, the following seems a bit ambiguous:

"Bill said "Hi!""

Is that the quoted string:

"Bill said " 

followed by the unquoted string:

Hi!

followed by a quoted blank string:

""

?