从subprocess.Popen逐行保存stdout到文件

9 投票
4 回答
8156 浏览
提问于 2025-04-16 12:50

我的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

撰写回答