从subprocess.Popen逐行保存stdout到文件
我的Python脚本使用subprocess来调用另一个脚本,但这个脚本输出的速度很慢,是一行一行输出的。我想要的是把输出逐行写入文件,而不是等整个过程结束后再把所有输出作为一个字符串写入文件。下面的代码是在“脚本”结束时才把输出写入“文件”。
args = ("script")
file = open('output.txt', 'w')
subprocess.Popen(args,stdout=file)
这样做可能吗?谢谢,Chris
相关问题:
4 个回答
1
我想分享一个不使用 .poll()、.wait() 或 .communicate() 的解决方案。这里有几点需要注意:
- 我使用
import codecs是因为我的输出包含东亚的 UTF-8 文本。 - 我用
try:来捕捉每一行,以过滤掉损坏或无效的 UTF-8 文本。 - 我使用
'\x0a'来强制在任何平台上都使用 Linux 的换行符。 - 如果你需要捕捉错误输出,可以使用
for line in iter(subproc.stderr.readline, ''):。 - 这种方法只有在子程序产生输出时才会生成输出。
- 虽然在这个例子中使用 kw 字典有点多余,但它展示了如何在 subprocess 中使用 **kwargs。
代码:
import subprocess
import codecs
import os
kw = {
'bufsize': 0,
'executable': None,
'stdin': subprocess.PIPE,
'stdout': subprocess.PIPE,
'stderr': subprocess.PIPE,
'preexec_fn': None,
'close_fds': False,
'shell': False,
'cwd': None,
'env': None,
'universal_newlines': False,
'startupinfo': None,
'creationflags': 0,
}
args = ['ls', '-lart']
kw['cwd'] = os.path.expanduser('~')
logfile = os.path.expanduser('~/stdout.txt')
stdlog = []
try:
subproc = subprocess.Popen(args,**kw)
except:
print 'Error loading subprocess. Check arguments and kwargs'
exit()
log = codecs.open(logfile,'w','utf-8')
log.write(': Starting log for: \"%s\"\x0a'%(' '.join(args)))
for line in iter(subproc.stdout.readline, ''):
try:
stdlog.append(line.rstrip().decode('utf-8'))
log.write(stdout[-1]+'\x0a')
print stdout[-1]
except:
pass
log.flush()
log.close()
1
是的,这是可能的。这里有一个我写的函数,用于测试工具,目的是对Python脚本进行单元测试。
def testrun(cmdline):
try:
cmdout, cmderr = "",""
cmdp = Popen(cmdline, shell=True,stdout=PIPE, stderr=PIPE)
cmdout,cmderr = cmdp.communicate()
retcode = cmdp.wait()
if retcode < 0:
print >>sys.stderr, "Child was terminated by signal", -retcode
else:
return (retcode,cmdout,cmderr)
except OSError, e:
return (e,cmdout,cmderr)
这个函数会返回一个元组,里面包含了三个东西:由sys.exit()发出的返回代码、标准输出的文本和标准错误输出的文本。它们都是文本字符串,所以你需要用splitlines把它们分成多行,才能进一步处理。
如果你真的需要逐行处理输出,那用pexpect可能会比使用subprocess模块更好。
2
你可以使用poll来与这个进程进行交互,这样你就可以尝试逐行与它互动:
比如说:
process = subprocess.Popen(["ls", "-lart"],
bufsize=-1, # fully buffered (default)
stdin=subprocess.PIPE,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
cwd=os.curdir,
env=os.environ)
my_stdout_file = open("stdout.txt", "w")
while True:
process.poll()
line = process.stdout.readline()
my_stdout_file.write(line)
eline = process.stderr.readline()
if line:
stdout_lines.append(line)
if eline:
stderr_lines.append(eline)
if (line == "" and eline == "" and
process.returncode != None):
break