在python中,如何检查subprocess.Popen对象中的stdout以查找任何内容?我正在编写一个有时会运行数小时的工具包装器。在子进程的stdout上使用.readline()会在运行超过几分钟时严重降低脚本的速度。如果有什么需要阅读,我需要一种更有效地检查标准输出的方法。顺便说一下,这个特殊工具一次只能写出完整的行。脚本是这样的:
#!/usr/bin/python -u
#thiswrap.py
import sys, time
from subprocess import *
chldp = Popen(sys.argv[1], bufsize=0, stdout=PIPE, close_fds=True)
chstdin,chstdout=chldp.stdin,chldp.stdout
startnoti=False
while not chldp.poll():
rrl=chstdout.readline() # <--- this is where the problem is
if rrl[-8:]=='REDACTED TEXT':
sys.stdout.write(rrl[:-1]+' \r')
if not startnoti: startnoti=True
else:
if startnoti: sys.stdout.write('\n')
sys.stdout.write(rrl)
if startnoti: # REDACTED
time.sleep(0.1)
time.sleep(0.1)
有什么想法吗?
答案 0 :(得分:4)
您需要将文件描述符设置为非阻止,您可以使用fcntl执行此操作:
import sys, time, fcntl, os
from subprocess import *
chldp = Popen(sys.argv[1], bufsize=0, stdout=PIPE, close_fds=True)
chstdin, chstdout = chldp.stdin, chldp.stdout
fl = fcntl.fcntl(chstdout, fcntl.F_GETFL)
fcntl.fcntl(chstdout, fcntl.F_SETFL, fl | os.O_NONBLOCK)
while chldp.poll() is not None:
try:
rrl = chstdout.readline()
except IOError:
time.sleep(0.1)
continue
# use rrl
如果没有可用数据,则IOError
会引发readline()
。
请注意,由于chldp.poll()
可以在子流程完成时返回0
,因此您应该在childp.poll() is not None
而不是while
中使用not childp.poll()
。
答案 1 :(得分:1)
可悲的是,没有现成的方法来轮询一个条件“管道中有足够的数据和换行符,以便readline()立即返回”。
如果您想要一次一行,并且不想阻止,您可以:
通过类或生成器实现自己的缓冲并通过它进行轮询,例如:
def linereader():
data = ""
while True:
if poll(f.fd):
data += f.read(100)
lines = data.split("\n")
data = lines[-1]
for line in lines[:-1]:
yield line
# use
for line in linereader():
if line:
print line
else:
time.sleep(...)
或者使用线程(作为练习留给读者,请注意旧版本的python bug,如果你从main以外的线程启动子进程)
答案 2 :(得分:0)
第一条评论中提出的解决方案几乎是正确的。您只需要将整数文件描述符作为第一个参数传递给 fcntl.fcntl
,而不是 Python 文件对象。取自 another answer
这是要更改的代码:
chstdout = chldp.stdout
fd = chstdout.fileno()
fl = fcntl.fcntl(fd, fcntl.F_GETFL)
fcntl.fcntl(fd, fcntl.F_SETFL, fl | os.O_NONBLOCK)