【问题标题】:Catch universal newlines but preserve original捕捉通用换行符但保留原始换行符
【发布时间】:2019-11-05 08:49:27
【问题描述】:

所以这是我的问题,

我正在尝试使用 Python 的 subprocess 模块编写一个运行另一个进程的简单程序,并且我想捕获该进程的实时输出。

我知道可以这样做:

proc = subprocess.Popen(cmd, stdout=subprocess.PIPE)

for line in iter(proc.stdout.readline, ""):
    line = line.rstrip()
    if line != "":
        print(line)

问题是,该过程可能会生成带有回车符\r 的输出,我想在我的程序中模拟这种行为。

如果我在Popen 中使用universal_newlines 标志,那么我可以捕获由回车生成的输出,但我不知道它是这样的,我只能“定期”打印它换行。我想避免这种情况,因为这可能会产生很多输出。

我的问题基本上是我是否可以像\n 一样捕捉\r 输出,但将其与实际\n 输出区分开来

编辑

这是我尝试过的一些简化代码:

文件download.py

import subprocess

try:
    subprocess.check_call(
        [
            "aws",
            "s3",
            "cp",
            "S3_LINK",
            "TARGET",
        ]
    )

except subprocess.CalledProcessError as err:
    print(err)
    raise SystemExit(1)

文件process_runner.py

import os
import sys

import subprocess

proc = subprocess.Popen(cmd, stdout=subprocess.PIPE)

for char in iter(lambda: proc.stdout.read(1), ""):
    sys.stdout.write(char)

download 中的代码使用aws s3 cp,它给出下载进度的回车。我想在我的程序process_runner 中模拟这种输出行为,它接收download 的输出。

起初我尝试迭代 readline 而不是 read(1)。由于忽略了 CR,这不起作用。

【问题讨论】:

  • IMO,如果您想将换行符视为有趣的数据,那么您不应该使用基于行的输入。您应该读取和缓冲数据块,然后自己扫描和处理\r\n 字符。您很有可能使用readline() 得到您想要的,但我认为这会使您的解决方案过于复杂。
  • @JohnHennig 您链接到的问题正是我写的我现在正在做的事情。问题是,确实,我不想要通用换行符。我希望我的程序能够像我运行的程序一样使用回车。问题是readline 一直读到遇到换行符
  • @Steve 这就是我尝试做的,逐个字符地阅读。从 PyCharm 运行时它运行正常,但由于某种原因,它在从终端运行时无法正常工作。虽然无法弄清楚问题是什么
  • @Zionsof,如果你在 PyCharm 和终端中得到不同的行为,那是一个明显的问题,如果我是你,我会解决或至少想了解。控制台输入是可能必须容忍行为差异的少数领域之一。也许你可以在这里详细说明一些代码,我们可以帮助你。
  • @Davis 我猜 stderr 上有一个进度指示器,它通过在更新状态之前打印回车来覆盖前一行来更新。输出中必须没有换行符才能正常工作。

标签: python subprocess


【解决方案1】:

一种可能的方法是使用 Popen 的二进制接口,既不指定 encoding 也不指定 error,当然也不指定 universal_newline。然后,我们可以在二进制流周围使用TextIOWrappernewline=''。因为 TextIOWrapper 的文档说:

...如果换行符是None...如果是'',则启用通用换行符模式,但行尾返回给调用者未翻译

(符合 PEP 3116)

您的原始代码可以更改为:

proc = subprocess.Popen(cmd, stdout=subprocess.PIPE)
out = io.TextIOWrapper(proc.stdout, newline='')

for line in out:
    # line is delimited with the universal newline convention and actually contains
    #  the original end of line, be it a raw \r, \n of the pair \r\n
    ...

【讨论】:

  • 与 Python2.7 不兼容,所以我不得不调整你的答案:out = io.open(proc.stdout.fileno(), mode='r', encoding="utf-8", newline='') 但效果很好。所以也许只是在答案中更新它
  • @Zionsof:我现在很少使用 Python 2.7,并且由于您的代码包含 print(err)(带括号),我假设您使用的是 Python 3。如果使用 Python2,请小心,因为 TextIOWrapper 会给你一个可能需要也可能不需要的 unicode 流。
  • 你说得对,只是想我会注意到它不适用于 2.7,需要根据我的评论进行更改
猜你喜欢
  • 2014-01-08
  • 2011-07-12
  • 2011-07-30
  • 1970-01-01
  • 1970-01-01
  • 2012-08-22
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多