【发布时间】:2023-03-13 07:04:01
【问题描述】:
有一个程序(我无法修改)创建两个输出文件。我正在尝试编写一个 Python 包装器来调用该程序,同时读取两个输出流,组合输出,并打印到标准输出(以促进流式传输)。我怎样才能做到这一点而不会死锁?下面的概念证明工作正常,但是当我将此方法应用于实际程序时,它会死锁。
概念证明:这是一个虚拟程序bogus.py,它创建两个输出文件,就像我要包装的程序一样。
#!/usr/bin/env python
from __future__ import print_function
import sys
with open(sys.argv[1], 'w') as f1, open(sys.argv[2], 'w') as f2:
for i in range(1000):
if i % 2 == 0:
print(i, file=f1)
else:
print(i, file=f2)
这里是 Python 包装器,它调用程序并组合它的两个输出(每次交错 4 行)。
#!/usr/bin/env python
from __future__ import print_function
from contextlib import contextmanager
import os
import shutil
import subprocess
import tempfile
@contextmanager
def named_pipe():
"""
Create a temporary named pipe.
Stolen shamelessly from StackOverflow:
http://stackoverflow.com/a/28840955/459780
"""
dirname = tempfile.mkdtemp()
try:
path = os.path.join(dirname, 'named_pipe')
os.mkfifo(path)
yield path
finally:
shutil.rmtree(dirname)
with named_pipe() as f1, named_pipe() as f2:
cmd = ['./bogus.py', f1, f2]
child = subprocess.Popen(cmd)
with open(f1, 'r') as in1, open(f2, 'r') as in2:
buff = list()
for i, lines in enumerate(zip(in1, in2)):
line1 = lines[0].strip()
line2 = lines[1].strip()
print(line1)
buff.append(line2)
if len(buff) == 4:
for line in buff:
print(line)
【问题讨论】:
-
您是否尝试过显而易见的:
subprocess.check_call(['/program', '-', '-']),如果program不理解'-',则通过'/dev/stdout'或'/dev/fd/1'或使用单个命名管道而不是'-'取决于系统。注意:如果子进程同时在自己的线程中写入文件,则输出可能会在一行中间交错。 -
是的,使用
/dev/stdout作为输出文件名时,输出的顺序有问题。它们确实需要被视为单独的流。 -
这里的“订单”是什么意思?你能举个例子吗? 1- 不同文件的写入顺序未定义(子进程外部没有之前或之后,例如,如果子进程写入:
write(fd1, 'a'); write(fd2, 'b')并且您正在从与fd1、fd2对应的文件中读取那么您无法知道'a'或'b'的优先顺序。2-如果您在谈论顺序,则可能是在谈论子进程中内部缓冲的表现... -
..[继续] 问题可能是标准输出缓冲。查看传递
/dev/stderr或/dev/tty是否有助于说服孩子将其输出线缓冲到文件中。如果您无法控制孩子的内部缓冲,当您可能从一个文件中看到一大块然后从另一个文件中看到一大块时,等等 -
我看到一个文件的大块,然后是另一个文件的大块,无论我是写入 stdout、stderr 还是 tty。
标签: python subprocess deadlock pipeline