【问题标题】:How to preserve python colorama color output when using tee on Windows在 Windows 上使用 tee 时如何保留 python colorama 颜色输出
【发布时间】:2020-02-10 17:32:46
【问题描述】:

在我的 Windows 10 场景中,我想将任意 python 控制台输出(print()sys.exit() 等)打印到控制台和日志文件。我无法控制代码的某些部分(外部 python 包),所以我不能使用一些专用的日志记录机制。

经过一番研究,我在 UnxUtils 中找到了 tee.exe 工具,它几乎可以按照我想要的方式完成这项任务。

我的问题是保留由 python 的 colorama 包生成的颜色。有没有办法做到这一点?目前,tee.exe 会去除颜色。

我正在寻找的答案不必依赖 tee.exe,它只是我最接近真正解决方案的方法。我正在寻找的应该执行以下操作:

  • 任何命令行输出都会同时出现在命令行和日志文件中(STOUT 和 STDERR)
  • 输出实时显示在命令行上。如果日志文件也是如此,则加分。
  • 颜色保留在命令行中。如果日志文件不包含任何与颜色相关的伪影,则可加分。

到目前为止,我所拥有的是:

Python 文件 teetest.py

import sys
import colorama

print("Test")
print("2nd Test")

colorama.init(autoreset=True)
print(colorama.Fore.RED + 'Color Test')
colorama.deinit(autoreset=True)

sys.exit("Error_Test")

批处理文件teetest.bat

@echo off
python teetest.py 2>&1 | tee log.txt
pause

我的输出看起来像这样(命令行和日志文件相同,没有颜色):

Test
2nd Test
Color Test
Error_Test

我正在寻找的解决方案会将上述内容打印到命令行,因此 Color Test 字样为红色。

编辑:

看来tee.exe 没有错。相反,colorama 会通过设计去除 ANSI 字符以进行颜色控制,因此在通过 tee.exe 时颜色会丢失。

来自色彩手册:

Colorama 通过包装标准输出、剥离它找到的 ANSI 序列(在输出中将显示为 gobbledygook)并将它们转换为适当的 win32 调用以修改终端的状态,也使这项工作在 Windows 上工作。

Colorama 的init() 函数提供参数strip,如果False,确实会导致colorama 不去除ANSI 字符。这反过来允许编写与tee.exe 相同的自定义tee.py,如下面的用户@martineau 所述。在其中,我们可以调用 colorama 并正确处理颜色。

这可能是一个可行的解决方案,但它仍然有一个缺点,即我必须在我的原始 python 代码中用 init(strip=False) 替换所有 colorama init() 调用,这反过来会导致 ANSI 字符出现在输出中如果代码在没有通过tee.py重定向的情况下被调用。

这实际上可能是我们可以在这里找到的最接近正确解决方案的地方。如果有人可以提供其他想法,我会全力以赴,但我担心机会渺茫。

【问题讨论】:

  • 问个简单的问题:您假设tee 剥离了ANSI 序列,但您真的测试 了吗? colorama itself 是终端感知的:“如果在 Windows 上或输出被重定向(不是 tty),默认行为是剥离。” github/colorama.
  • @usr2564301。也许我没有正确表达这一点。我不认为tee 会去除 ANSI 序列,但实际上 colorama 会。请参阅我对 martineau 答案的评论。我打算将这些信息整合到原始帖子中,但还没有开始。一个解决方案可能是使用 colorama 的 strip 参数,但这会迫使我调整 python 代码,这是我想避免的。
  • 如果您正在寻找对您的代码影响最小的更改,您可以随时将https://github.com/tartley/colorama/blob/master/colorama/initialise.py的本地副本更改为默认清除strip
  • @usr2564301 感谢您的想法。实际上,我以类似的方式研究了如何破解 colorama 并发现 strip 使得它很容易做到。不管我是否通过tee 管道输出,如果有一个选项以相同的方式工作,仍然会非常酷。不过,在这一点上,我希望渺茫。

标签: python windows tee colorama


【解决方案1】:

我不知道它对colorama 的作用如何,但是在对我在网上找到的几个用于 Windows 的tee 实用程序不满意后,我最终用 Python (3.x) 编写了自己的实用程序。

您可能需要对其进行修改以满足自己的需要,但这应该是一个好的开始。

mytee.py

"""
    Copies stdin to stdout (screen) *and* the specified file.
"""
import fileinput
import os
from pathlib import Path
import sys

SHOW_FULL_PATH = False
DIVIDER = True
DIV_CH = ' '

if len(sys.argv) != 2:
    raise SystemExit('Usage: mytee <filepath>')

try:
    inp = fileinput.input(())  # Read from stdin.
    path = Path(sys.argv[1])
    stdout_write = sys.stdout.write
    stdout_flush = sys.stdout.flush
    # Assumes .py in same dir as output file.
    script = (f'{path.parent/path.stem}{path.suffix}' if SHOW_FULL_PATH else
              f'{path.stem}{path.suffix}')

    with open(path, 'w') as outp:  # Write to specified file.
        outp_write = outp.write
        outp_flush = outp.flush

        def write(line):
            stdout_write(line)
            outp_write(line)

        def writeln(line):
            write(line + '\n')

        banner = f'"{script}"' if ' ' in script else f'-[{script}]-'
        writeln(f'{banner}')
        if DIVIDER:
            writeln(f'{DIV_CH * len(banner)}')
        for line in inp:
            write(line)
        if DIVIDER:
            writeln(f'{DIV_CH * len(banner)}')
        writeln('-[done]-')
finally:
    inp.close()  # Not sure this is really necessary.

sys.exit(0)

【讨论】:

  • 这是一个很酷的主意!从我对您的代码的测试运行来看,它似乎也去除了颜色信息。 colorama 很可能会在打印时消耗颜色信息,因此该部分永远不会传递给 stout。也许我可以在 python 中找到另一种方法来捕获该数据。感谢您的想法!
  • Windows 的管道命令 | 可能是剥离颜色信息的原因。
  • 可能有办法,根据您的解决方案。 colorama.init() 具有可选属性“strip”。如果设置为 False,它将保留控制字符。因此,如果我在您的代码中添加一个 colorama.init() 并将 colorama.init() 替换为我的 colorama.init(strip=False) ,那么它就可以工作。这仍然不理想,因为我必须更改代码以适应更改,但这是朝着正确方向迈出的一步。
  • Cerno:很高兴听到我的“一次性”实用程序被证明对其他人有用。随时发布您自己的答案,其中包含支持colorama 的更改供其他人使用。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-07-03
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-03-20
  • 2017-01-07
相关资源
最近更新 更多