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

发布于 2024-10-19 23:54:52 字数 228 浏览 1 评论 0原文

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

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

有可能吗?谢谢克里斯

My python script uses subprocess to call an another script, which produces output very slow(line-by-line basis). I would like to write the output line by line to file not when the whole process ends and writes the entire output as string.The following code writes the output to "file" when the "script" ends.

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

Is it even possible ? Thanx, Chris

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

筑梦 2024-10-26 23:54:52

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

You can interact with the process using poll so that you can attempt to interact with it line by line:

For example:

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
秋凉 2024-10-26 23:54:52

是的,这是可能的。这是我为测试工具编写的一个函数,用于对 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 模块。

Yes, it is possible. Here is a function that I wrote for a test harness use to do unit testing of Python shell scripts.

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)

The function returns a tuple which contains the shell return code issues by sys.exit(), the standard output text, and the standard error output text. They are both text strings so you would need to use splitlines to break them into lines before processing.

If you really need to interact with the output, line by line, then it is probably better to use pexpect rather than the subprocess module.

2024-10-26 23:54:52

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

  • 我使用 import codecs 因为我的输出包含东亚 UTF-8 文本
  • 我用 try: 捕获每一行以过滤掉损坏/无效的 UTF-8 文本
  • 无论平台如何,我都使用 '\x0a' 强制 Linux 换行。
  • 如果需要捕获 stderr,请使用 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()

Thought I'd share a solution that doesn't use .poll(), .wait() or .communicate(). A couple of points:

  • I use import codecs because my output includes East Asian UTF-8 text
  • I trap each line with try: to filter out corrupted/invalid UTF-8 text
  • I use '\x0a' to force Linux newline regardless of the platform.
  • Use for line in iter(subproc.stderr.readline, ''): if you need to capture stderr
  • This approach generates output only when child program creates output
  • Using the kw dictionary is overkill for this example, but shows how to use **kwargs with subprocess

Code:

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()
荆棘i 2024-10-26 23:54:52

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

不幸的是,它涉及从输出流一次读取一个字符,累积行,直到找到换行符。不过,它确实有效,而且我不知道还有什么其他方法可以达到相同的行为。

I had the same problem for a programming language I'm working on, and ended up doing this: https://github.com/perimosocordiae/plumbum/blob/master/lib/stdlib.py#L21

Unfortunately, it involves reading from the output stream a character at a time, accumulating the line until a newline is found. It works, though, and I don't know of any other way to get the same behavior.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文