为啥不在 Python 中的 subprocess.Popen 中使用 `shell=True`? [复制]

Posted

技术标签:

【中文标题】为啥不在 Python 中的 subprocess.Popen 中使用 `shell=True`? [复制]【英文标题】:Why not just use `shell=True` in subprocess.Popen in Python? [duplicate]为什么不在 Python 中的 subprocess.Popen 中使用 `shell=True`? [复制] 【发布时间】:2012-11-09 14:15:07 【问题描述】:

我有一个很长的单行 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) 我不应该以“第一方式”写作的最令人信服的原因可能是什么

【问题讨论】:

“第一种方式”几乎无法阅读。所以这是一个很好的理由不使用它。 @ebarr 由于 awk 部分,它无法读取... 顺便说一句,在第二种方式中,可能需要关闭所有不再被主程序使用的中间文件描述符:p1.stdout.close() 在创建 p2 之后,等等在。这样可以确保进程 1 获得进程 2 创建的 EOF 条件。 【参考方案1】:

如果您的输入数据来自不受信任的来源,则使用 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) 如果你不使用format(),那么你应该在cmd 中取消 并添加% 指令、变量。否则命令被破坏。 (2) line 是单个字符,而不是代码中的行。 check_output() 返回一个 single 字符串。您不想一次迭代一个字符。将子进程的标准输出重定向到文件:check_call(cmd, shell=True, stdout=file)【参考方案2】:

(1)“第二种方式”是否会比“第一种方式”慢

启动一个新进程是一项昂贵的操作,因此在允许 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)

它在您的命令中实现> 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?

【讨论】:

【参考方案3】:

它不太可能更慢,但您始终可以使用timeit 进行测试以确保它。不采用第一种方式有两个很好的理由。第一个是虽然第一次打字可能会稍微快一点,但可读性大大降低,Readability Counts。第二个是shell=True是huge security risk,原则上应该避免使用。

【讨论】:

以上是关于为啥不在 Python 中的 subprocess.Popen 中使用 `shell=True`? [复制]的主要内容,如果未能解决你的问题,请参考以下文章

使用 subprocess.Popen() 在 python 脚本中设置 PYTHONPATH 失败 [重复]

为啥 Plotly(在 Python3 中)不在折线图中制作不同的线?

为啥 ./configure 在 python setup.py 中不起作用?

Python中的Subprocess模块

python中的subprocess.Popen()使用

python中的subprocess.Popen | 9