我试图通过移植一个通过
调用多个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()方法。
我真的很困惑。看来我的选择是:
call
与shell=True
一起使用(安全风险,他们说)PIPE
与call
一起使用(但风险死锁)Popen
和communicate
(但我的数据太大,100兆字节)。我错过了什么?如何在Python中为没有shell=True
或shell=True
可接受的非常大的文件创建多个流程管道?
答案 0 :(得分:2)
关于&#34;在内存中缓冲的注释&#34;只有使用像stdout=PIPE
这样的东西才有意义。它不适用于stdout=file
(os.dup2()
在OS文件描述符级重定向,.communicate()
方法没有任何关系。)
请勿使用call
和PIPE
。 call()
只是(简化)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?