在Python中,如何检查subprocess.Popen对象的stdout是否有可读内容?
在Python中,我该如何检查subprocess.Popen对象的标准输出(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)
有什么好主意吗?
3 个回答
0
第一个评论里提到的解决方案几乎是对的。你只需要把一个整数类型的文件描述符作为第一个参数传给 fcntl.fcntl
,而不是传递Python的文件对象。这是从 另一个回答 中得到的。
下面是需要修改的代码:
chstdout = chldp.stdout
fd = chstdout.fileno()
fl = fcntl.fcntl(fd, fcntl.F_GETFL)
fcntl.fcntl(fd, fcntl.F_SETFL, fl | os.O_NONBLOCK)
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)。
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
当没有数据可用时,调用 readline()
会抛出一个 IOError
错误。
需要注意的是,由于 chldp.poll()
在子进程结束时可能会返回 0
,所以在你的 while
循环中,最好使用 childp.poll() is not None
,而不是 not childp.poll()
。