Python沟通vs shell = True

时间:2014-09-11 01:51:43

标签: python shell subprocess

我试图通过移植一个通过

调用多个shell命令行的Python脚本来做正确的事情
subprocess.call(... | ... | ... , shell=True) 

使用shell=True来避免Popen的安全风险。所以我写了一个小样本脚本来试试。它执行命令行

awk '{print $1 " - " $2}' < scores.txt | sort | python uppercase.py > teams.txt

如下:

with open('teams.txt', 'w') as destination:
    with open('scores.txt', 'r') as source:
        p3 = Popen(['python', 'uppercase.py'], stdin=PIPE, stdout=destination)
        p2 = Popen(['sort'], stdin=PIPE, stdout=p3.stdin)
        p1 = Popen(['awk', '{print $1 " - " $2}'], stdin=source, stdout=p2.stdin)
        p1.communicate()

此程序适用于小型数据集。

现在我对communicate方法的文档中的以下行感到震惊:

  

注意读取的数据缓冲在内存中,因此如果数据量很大或无限制,请不要使用此方法。

什么?但我有大量的文件需要awk&d; d和其他东西。我首先尝试使用communicate的原因是我在subprocess.call看到了这个警告:

  

注意不要将stdout = PIPE或stderr = PIPE与此函数一起使用,因为它可能会基于子进程输出卷死锁。需要管道时,请使用Popen和communic()方法。

我真的很困惑。看来我的选择是:

  • callshell=True一起使用(安全风险,他们说)
  • PIPEcall一起使用(但风险死锁)
  • 使用Popencommunicate(但我的数据太大,100兆字节)。

我错过了什么?如何在Python中为没有shell=Trueshell=True可接受的非常大的文件创建多个流程管道?

1 个答案:

答案 0 :(得分:2)

关于&#34;在内存中缓冲的注释&#34;只有使用像stdout=PIPE这样的东西才有意义。它不适用于stdout=fileos.dup2()在OS文件描述符级重定向,.communicate()方法没有任何关系。)

请勿使用callPIPEcall()只是(简化)Popen().wait(),即它不会从管道中读取。除非你读取(写入)管道(没有任何意义),否则不要使用PIPE

在您的代码p1.communicate()中,您无法读取任何数据。您可以将其替换为p1.wait()。您的代码缺失p3.stdin.close(); ......; p2.stdin.close(); ......; p3.wait(),p2.wait()

否则,代码适用于大文件。

shell=True

如果命令是硬编码的(如您的问题中所示)那么就没有安全风险。如果命令可能来自不受信任的来源,那么如何运行此命令并不重要(在这种情况下,不受信任的源可能会运行它喜欢的任何内容)。如果只有一些参数来自不受信任的来源,那么您可以使用plumbum模块来避免自己重新实现管道:

from plumbum.cmd import awk, sort, python

(awk['{print $1 " - " $2}'] < untrusted_filename | sort |
 python["uppercase.py"] > "teams.txt")()

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