On Windows, how can I protect arguments to shell scripts using Python 2.7 subprocess?
Consider for example the following Python code:
subprocess.call([r'.\tst.bat', '"1|2"'])
Here I've put double quotes around the argument to tst.bat, in order to protect '|' from the shell since tst.bat will ultimately be run via the Windows shell. However, if there are double quotes in arguments, subprocess in开发者_开发技巧 Python 2.7 escapes them. The result is that tst.bat receives this argument: \"1|2\".
How can I escape the argument "1|2" so that it's passed untransformed to tst.bat?
Subprocess module on Windows in case of a list of arguments given to the call
method transforms this list into a string using list2cmdline
function. In the docstring of this function one can read:
3) A double quotation mark preceded by a backslash is interpreted as a literal double quotation mark.
According to this the solution of your problem should look like this
subprocess.call([r'.\tst.bat', r'\"1|2\"'])
However it seems like there's a mismatch between documentation and implementation of list2cmdline
function;
Python 2.7.4 (default, Apr 6 2013, 19:55:15) [MSC v.1500 64 bit (AMD64)] on win32
Type "help", "copyright", "credits" or "license" for more information.
>>> import subprocess
>>> print subprocess.list2cmdline([r'\"1|2\"'])
\\\"1|2\\\"
The same behavior is in Python 3.3.1. I raised a bug at http://bugs.python.org/issue18649
UPDATE
It turns out I was confused by very misleading docstring of list2cmdline
function - see http://bugs.python.org/issue18649 for details.
The sequence of arguments passed to subprocess.call
is translated by list2cmdline
function which is concerned only with the quoting mechanism used by MS C runtime and not by quoting mechanism used by cmd.exe. This makes passing arguments - which are later to be interpreted by cmd.exe - to subprocess.call
as a list either very awkward or impossible.
There's one more chance to get it working in this case, though. list2cmdline
double quotes every argument with a space or tab inside. If having a space at the end of your argument does not break the batch file you can add it at the end forcing double quoting as you wanted.
>>> import subprocess
>>> print subprocess.list2cmdline(['1|2 '])
"1|2 "
This (or a similar problem) has been reported as a bug.
References:
- http://bugs.python.org/issue2304
- http://bugs.python.org/issue11139
- http://bugs.python.org/issue8972
精彩评论