具有复杂参数的Python子进程

时间:2013-05-29 08:41:34

标签: python python-3.x subprocess

我正在寻找从python(3)调用shell命令的最安全,最方便的方法。这里是ps到pdf的转换:

 gs -dBATCH -dNOPAUSE -sDEVICE=pdfwrite -sOutputFile="${pdf_file}" "${ps_file}"

我使用subprocessshlex并避免使用shell=True。 但我发现结果命令不一致:

cmd = ['gs', '-dBATCH', '-dNOPAUSE', '-sDEVICE=pdfwrite', '-sOutputFile={0}'.format(pdf_filename), ps_filename]

我错过了什么?! <{1}}语法看起来很干净,用空格分隔的参数,在其他地方看起来一团糟。

subprocess.call()(在python级别,即。转义,注入保护,引用等)之间调用时有什么区别:

subprocess.call(cmd)

如果没有,这也是一个很好的方法吗?

cmd = ['do', '--something', arg]
cmd = ['do', '--someting {0}'.format(arg)]

另一个不一致的例子:

cmd = ['gs', '-dBATCH -dNOPAUSE -sDEVICE=pdfwrite -sOutputFile={0} {1}'.format(pdf_filename, ps_filename)] 将为hg merge -r 3

cmd = ['hg', 'merge', '-r', revision_id]将为hg merge --rev=3

尽管如此,发送相同参数的方法有两种。

2 个答案:

答案 0 :(得分:6)

不同之处在于命令可能有一个--something选项,它接受一个参数,但它没有--something foo选项 - 这就是你要说的。当您在shell中运行命令时,如wc -l myfile.txt,您的shell会在找到空格的命令行中拆分 - 所以运行的命令是['wc', '-l', 'myfile.txt']

subprocess模块不执行此类拆分。你必须自己做(除非你使用'shell'选项,但这通常不太安全,所以如果可以的话,请避免使用它。)。

一些反例......

尝试运行名为“wc -l myfile.txt”的命令。当然,没有安装“wc -l myfile.txt”命令,只有“wc”命令,所以这会失败:

['wc -l myfile.txt']

尝试使用选项“-l myfile.txt”运行命令“wc”。有一个“-l”选项,但没有“-l myfile.txt”选项。这将失败:

['wc', '-l myfile.txt']

和一个正确的例子:

['wc', '-l', 'myfile.txt']

这会使用-l选项(仅打印行数)和myfile.txt作为唯一文件名来调用wc。

你可能会发现令人困惑的是像这样的碎片:

'-sOutputFile={0}'

这是一种给出选项参数的'内联'样式。如果支持,程序的帮助通常会明确说明。 Python不会拆分它们 - 接收它们的程序会这样做。

“内联”参数有三种主要风格。我将使用grep选项演示前两个:

--context=3
-C3

(以上两行相同)

第三种类型只能在imagemagick中找到,而其他一些程序往往会有大量的命令行参数,例如gs:

-sOutputFile=foo

这只是GNU标准的一个小变化 - 上面显示的long-option = VALUE表单。

The GNU libc manual's "argument syntax" section提供了对这些选项传递约定的完整解释。

  • 关于转义:没有转义,通常不需要转义。字符串值完全按照您指定的传递给命令。当然,没有引用也不需要引用,因为你已经在Python代码中处理了这个问题。

  • 关于注射:除非使用'shell'选项,否则这是不可能的。 请勿使用'shell'选项:)。

答案 1 :(得分:2)

你问的问题之间的区别..容易检查:

arg = 'foo'

cmd = ['do', '--something', arg]
print cmd
cmd = ['do', '--someting {0}'.format(arg)]
print cmd

>>> 
['do', '--something', 'foo']
['do', '--someting foo']

你可以看到它们不一样。

为了正确调用您的子进程,您应该这样做:

cmd = ['gs', '-dBATCH', '-dNOPAUSE', '-sDEVICE=pdfwrite', '-sOutputFile={0}'.format(pdf_filename), ps_filename]
subprocess.Popen(cmd, ...)

OR:

cmd = 'gs -dBATCH -dNOPAUSE -sDEVICE=pdfwrite -sOutputFile={0} {1}'.format(pdf_filename, ps_filename)
subprocess.Popen(cmd, shell=True, ...)

使用参数列表或字符串之间的区别:

当您使用参数列表时,您将这些作为参数传递给shell(如果您指定,则传递给它)

当您发送带有shell=True的字符串时,您让shell解析字符串并创建自己的参数...

所以['do', '--something', 'foo']是3个参数,而['do', '--someting foo']只有2个参数。