【问题标题】:Saving stdout from subprocess.Popen to file line by line将标准输出从 subprocess.Popen 逐行保存到文件
【发布时间】:2011-03-02 17:45:20
【问题描述】:

我的 python 脚本使用 subprocess 调用另一个脚本,它产生的输出非常慢(逐行)。我想在整个过程结束时将输出逐行写入文件而不是将整个输出写入字符串。以下代码在“脚本”结束时将输出写入“文件”。

args = ("script")
file = open('output.txt', 'w')
subprocess.Popen(args,stdout=file)

这甚至可能吗?谢谢,克里斯

【问题讨论】:

    标签: python subprocess


    【解决方案1】:

    您可以使用 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
    

    【讨论】:

      【解决方案2】:

      是的,这是可能的。这是我为测试工具编写的一个函数,用于对 Python shell 脚本进行单元测试。

      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() 的shell 返回代码问题、标准输出文本和标准错误输出文本。它们都是文本字符串,因此您需要在处理之前使用splitlines 将它们分成几行。

      如果您确实需要逐行与输出交互,那么使用pexpect 可能比subprocess 模块更好。

      【讨论】:

      • 你能举一个使用 pexpect 的例子吗?
      • 你去pexpect网站了吗?第 8 节展示了几个如何使用它的示例。
      【解决方案3】:

      我想分享一个不使用 .poll()、.wait() 或 .communicate() 的解决方案。几点:

      • 我使用 import codecs,因为我的输出包含东亚 UTF-8 文本
      • 我用try: 捕获每一行以过滤掉损坏/无效的UTF-8 文本
      • 无论平台如何,我都使用'\x0a' 强制 Linux 换行。
      • 如果您需要捕获标准错误,请使用for line in iter(subproc.stderr.readline, ''):
      • 此方法仅在子程序创建输出时才生成输出
      • 在此示例中使用 kw 字典是多余的,但显示了如何将 **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()
      

      【讨论】:

        【解决方案4】:

        对于我正在研究的编程语言,我遇到了同样的问题,最终这样做了:https://github.com/perimosocordiae/plumbum/blob/master/lib/stdlib.py#L21

        不幸的是,它涉及一次从输出流中读取一个字符,累积该行直到找到换行符。不过,它可以工作,而且我不知道有任何其他方法可以获得相同的行为。

        【讨论】:

        • 请检查我添加到答案中的 pexpect 链接。
        • 看来 pexpect 还不兼容 py3k。
        猜你喜欢
        • 2011-03-12
        • 2020-11-25
        • 2017-08-11
        • 2019-02-27
        • 2016-12-04
        • 2022-01-17
        • 2010-12-21
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多