为了简化我的问题,这里是一个python脚本:
from subprocess import Popen, PIPE
proc = Popen(['./mr-task.sh'], shell=True, stdout=PIPE, stderr=PIPE)
while True:
out = proc.stdout.readline()
print(out)
这是mr-task.sh
,它启动了mapreduce工作:
hadoop jar xxx.jar some-conf-we-don't-need-to-care
当我运行./mr-task
时,我可以在屏幕上看到日志,例如:
14/12/25 14:56:44 INFO util.NativeCodeLoader: Loaded the native-hadoop library
14/12/25 14:56:44 INFO snappy.LoadSnappy: Snappy native library loaded
14/12/25 14:57:01 INFO mapred.JobClient: Running job: job_201411181108_16380
14/12/25 14:57:02 INFO mapred.JobClient: map 0% reduce 0%
14/12/25 14:57:28 INFO mapred.JobClient: map 100% reduce 0%
但我无法让这些输出运行python脚本。我尝试删除shell=True
或获取stderr,仍然没有任何内容。
有没有人知道为什么会这样?
答案 0 :(得分:3)
您可以将stderr重定向到stdout:
from subprocess import Popen, PIPE, STDOUT
proc = Popen(['./mr-task.sh'], stdout=PIPE, stderr=STDOUT, bufsize=1)
for line in iter(proc.stdout.readline, b''):
print line,
proc.stdout.close()
proc.wait()
请参阅Python: read streaming input from subprocess.communicate()。
在我的真实程序中,我将stderr重定向到stdout并从stdout读取,因此不需要bufsize,是吗?
将stderr重定向到stdout和bufsize
是不相关的。更改bufsize
可能会影响时间性能(默认bufsize = 0,即在Python 2上无缓冲)。无缓冲I / O可能慢10到100倍。像往常一样,如果重要的话,你应该衡量时间表现。
在子进程终止后调用Popen.wait / communication只是为了清除僵尸进程,这两种方法在这种情况下没有区别,对吗?
不同之处在于proc.communicate()
在收割子进程之前关闭了管道。它释放文件描述符(有限资源)以供程序中的其他文件使用。
关于缓冲区,如果输出填充缓冲区maxsize,子进程会挂起吗?这是否意味着如果我使用默认的bufsize = 0设置我需要尽快从stdout读取,以便子进程不阻塞?
没有。这是一个不同的缓冲区。 bufsize
控制调用.readline()
方法时填充/排空的父级内的缓冲区。无论bufsize
是什么,都不会出现僵局。
无论孩子可能产生多少输出,代码(如上所述)都不会死锁。
@falsetru's answer中的代码可能会死锁,因为它会创建两个管道(stdout=PIPE, stderr=PIPE
),但它只能从一个管道中读取{{1 }})。
在子节点和父节点之间有几个缓冲区,例如,C stdio的stdout缓冲区(子进程内的libc缓冲区,父节点无法访问),子节点的stdout OS管道缓冲区(内核,父进程可以从中读取数据)这里)。这些缓冲区是固定的,如果您将更多数据放入其中,它们将不会增长。如果stdio的缓冲区溢出(例如,在proc.stderr
调用期间),则数据被下游推送到子节点的stdout OS管道缓冲区中。如果没有人从管道读取然后该OS管道缓冲区填满并且子块阻塞(例如,在printf()
系统调用上)尝试刷新数据。
具体来说,我假设是基于C stdio的程序和POSIXy操作系统。
发生死锁是因为父级尝试读取空的stderr管道,因为孩子正忙着尝试刷新其标准输出。因此,这两个过程都会挂起。
答案 1 :(得分:0)
一个可能的原因是输出打印到标准错误而不是标准输出。
尝试将stdout
替换为stderr
:
from subprocess import Popen, PIPE
proc = Popen(['./mr-task.sh'], stdout=PIPE, stderr=PIPE)
while True:
out = proc.stderr.readline() # <----
if not out:
break
print(out)