为什么不在Python的subprocess.Popen中使用`shell = True`?

时间:2012-11-21 10:37:31

标签: python linux shell process subprocess

我有一个很长的单行shell命令可以被Python调用。代码是这样的:

# "first way"
def run_cmd ( command ):
    print "Run: %s" % command
    subprocess.call (command, shell=True)
run_cmd('''sort -n -r -k5 {3} |head -n 500|awk 'OFS="\t"{{if($2-{1}>0){{print $1,$2-{1},$3+{1},$4,$5}}}}' > {2}'''.format(top_count,extend/2,mid,summit))

这些代码有效,但总是这样抱怨:

sort: write failed: standard output: Broken pipe
sort: write error
awk: (FILENAME=- FNR=132) fatal: print to "standard output" failed (Broken pipe)

根据a previous answer,我需要使用更长的脚本来完成此操作,例如:

# "second way"
p1 = Popen("sort -n -r -k5 %s"%summit, stdout=PIPE)
p2 = Popen("head -n 500", stdin=p1.stdout, stdout=PIPE)
# and so on ..........

我的问题是:

(1)“第二路”是否比“第一路”慢

(2)如果我不得不以“第一路”写作(因为写作速度更快),我怎样才能避免像broken pipe

这样的抱怨

(3)我不应该用“第一种方式”写出最令人信服的理由

3 个答案:

答案 0 :(得分:5)

如果您的输入数据来自不受信任的来源,则使用shell = True可能会带来安全风险。例如。如果您的mid变量的内容为"/dev/null; rm -rf /",该怎么办?在你的场景中似乎并非如此,所以我不会太担心它。

在您的代码中,您将awk的结果直接写入mid中的文件名。要调试问题,您可能希望使用subprocess.check_output并从python程序中的awk调用中读取结果。

cmd = """sort -n -r -k5 %s |
      head -n 500|
      awk 'OFS="\t"{{if($2-{1}>0){{print $1,$2-{1},$3+{1},$4,$5}}}}'""".format(summit, top_count)

subprocess.check_call(cmd, shell=True, stdout=file)

答案 1 :(得分:2)

  

(1)是否"第二种方式"将比第一种方式慢#34;

启动一个新进程是一项昂贵的操作,因此在允许shell解析命令行和启动子进程并在Python中自己执行它之间应该没有太大区别。唯一重要的基准是硬件上的代码。测量它。

  

(2)如果我必须写"第一路"无论如何(因为它写得更快),我怎样才能避免像管道破裂那样的抱怨

第一个破裂的管道"可能类似于:'yes' reporting error with subprocess communicate()。试试the workaround I've provided there

您可以通过将管道标准输出重定向到mid文件来修复第二个损坏的管道:

with open(mid, 'wb') as file:
    check_call(pipeline, shell=True, stdout=file)

它在没有shell的命令中实现> {2}

  

(3)可能是我不应该写的最有说服力的理由"第一种方式"

如果top_countextendmidsummit中的任何一个来自不完全受您控制的来源,那么您冒险在用户下运行任意命令


plumbum模块提供安全性和可读性(如果在这种情况下对您来说很重要,则衡量时间性能):

from plumbum.cmd import awk, head, sort

awk_cmd = 'OFS="\t"{if($2-%s>0){print $1,$2-%s,$3+%s,$4,$5}}' % (extend/2,)*3
(sort["-n", "-r", "-k5", summit] | head["-n", "500"] | awk[awk_cmd] > mid)()

请参阅How do I use subprocess.Popen to connect multiple processes by pipes?

答案 2 :(得分:1)

它不太可能变慢,但您可以随时使用timeit对其进行测试。第一种方式有两个很好的理由不这样做。首先是虽然第一次键入的速度可能略快,但可读性大大降低,Readability Counts。第二个是使用shell=Truehuge security risk,作为委托人应该避免使用。