1

我正在寻找从 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]

我想念什么?!subprocess.call()用空格分隔的参数语法看起来很干净,在其他地方看起来很乱。

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

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将会cmd = ['hg', 'merge', '--rev={0}'.format(revision_id)]

尽管如此,这是发送相同参数的两种方式。

4

2 回答 2

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']

这将调用 wc-l选项(仅打印行数)并myfile.txt作为唯一的文件名。

您可能会感到困惑的是这样的片段:

'-sOutputFile={0}'

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

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

--context=3
-C3

(上面两行是等价的)

第三种类型仅在 imagemagick 和其他一些具有大量命令行参数的程序中找到,例如 gs:

-sOutputFile=foo

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

GNU libc 手册的“参数语法”部分对这些选项传递约定进行了完整的解释。

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

  • 关于注入:除非您使用“shell”选项,否则这是不可能的。不要使用'shell'选项:)。

于 2013-05-29T09:11:46.920 回答
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, ...)

或者:

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

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

当您使用参数列表时,您将这些参数作为参数传递给 shell(或可执行文件,如果您指定)

当你发送一个字符串时,shell=True让shell解析字符串并生成自己的参数......

3 个参数也是如此['do', '--something', 'foo'],而['do', '--someting foo']只有 2 个参数。

于 2013-05-29T08:46:19.287 回答