我正在寻找从python(3)调用shell命令的最安全,最方便的方法。这里是ps到pdf的转换:
gs -dBATCH -dNOPAUSE -sDEVICE=pdfwrite -sOutputFile="${pdf_file}" "${ps_file}"
我使用subprocess
,shlex
并避免使用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
尽管如此,发送相同参数的方法有两种。
答案 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个参数。