来自http://docs.python.org/3/library/subprocess.html#replacing-shell-pipeline
的代码段output=`dmesg | grep hda`
# becomes
p1 = Popen(["dmesg"], stdout=PIPE)
p2 = Popen(["grep", "hda"], stdin=p1.stdout, stdout=PIPE)
p1.stdout.close() # Allow p1 to receive a SIGPIPE if p2 exits.
output = p2.communicate()[0]
问题:我不太明白为什么需要这一行:p1.stdout.close()
?
如果通过这样做p1 stdout甚至在完全输出数据并且p2仍然存活之前就关闭了怎么办?这么快就关闭p1.stdout
我们不是冒险吗?这是如何工作的?
答案 0 :(得分:2)
p1.stdout.close()
关闭文件描述符的Python的副本。 p2
已打开该描述符(通过stdin=p1.stdout
),因此关闭Python的描述符不会影响p2
。但是,现在管道端只打开一次,所以当它关闭时(例如,如果p2
死亡),p1
将看到管道关闭并将获得SIGPIPE
。
如果你没有在Python中关闭p1.stdout
,并且p2
死了,那么p1
将没有信号,因为Python的描述符将保持管道打开。
答案 1 :(得分:1)
管道是进程外部的(它是操作系统的东西),并且由使用读写句柄的进程访问。许多进程可以拥有管道的句柄,如果管理不当,可以以各种灾难性的方式进行读写。当管道的所有手柄都关闭时,管道关闭。
虽然在Linux和Windows中流程执行的工作方式不同,但基本上会发生这种情况(我会被杀死!)
p1 = Popen(["dmesg"], stdout=PIPE)
创建pipe_1,将dmesg的写句柄作为其标准输出,并将父句中的读句柄返回为p1.stdout。您现在有一个带有2个句柄的管道(pipe_1在dmesg中写入,pipe_1在父项中读取)。
p2 = Popen(["grep", "hda"], stdin=p1.stdout, stdout=PIPE)
创建pipe_2。为grep提供pipe_2的写入句柄和pipe_1的读取句柄的副本。您现在有2个管道和5个句柄(pipe_1在dmesg中写入,pipe_1读取和pipe_2在grep中写入,pipe_1读取和pipe_2在父级中读取)。
p1.stdout.close() # Allow p1 to receive a SIGPIPE if p2 exits.
请注意,pipe_1有两个读句柄。您希望grep具有读取句柄,以便它读取dmesg数据。您不再需要父级中的句柄。关闭它,以便pipe_1上只有1个读取句柄。如果grep死掉,它的pipe_1读取句柄被关闭,操作系统注意到pipe_1没有剩余的读取句柄,并给dmesg带来了坏消息。
output = p2.communicate()[0]
dmesg将数据发送到stdout(pipe_1写入句柄),开始填充pipe_1。 grep读取清空pipe_1的stdin(pipe_1读取句柄)。 grep还写入stdout(pipe_2写句柄)填充pipe_2。父进程读取pipe_2 ...你得到了一个管道!