在Windows上使用tee时如何保留python colorama颜色输出

在Windows上使用tee时如何保留python colorama颜色输出,python,windows,tee,colorama,Python,Windows,Tee,Colorama,在我的Windows 10场景中,我希望将任意python控制台输出(print(),sys.exit(),等等)打印到控制台和日志文件中。我无法控制代码的某些部分(外部python包),因此无法使用某些专用的日志机制 经过一些研究,我在UnxUtils中找到了工具tee.exe,它几乎按照我想要的方式完成了这个任务 我的问题是保留python的colorama包生成的颜色。有没有办法做到这一点?目前,tee.exe会去除颜色 我正在寻找的答案不必依赖于tee.exe,它只是我最接近真正解决方案

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

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

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

我正在寻找的答案不必依赖于tee.exe,它只是我最接近真正解决方案的地方。我要找的应该是:

  • 任何命令行输出都显示在命令行和日志文件中(STOUT和STDERR)
  • 输出实时显示在命令行上。如果日志文件也是如此,则可获得额外点数
  • 颜色保留在命令行上。如果日志文件不包含任何与颜色相关的工件,则可获得额外点数
到目前为止,我得到的是:

Python文件teetest.py:

批处理文件teetest.bat:

我的输出如下所示(命令行和日志文件相同,没有颜色):

我正在寻找的解决方案将把上面的内容打印到命令行中,以便单词Color Test为红色

编辑:

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

从colorama手册:

Colorama通过包装stdout、剥离它找到的ANSI序列(在输出中显示为gobbledygook)并将它们转换为适当的win32调用来修改终端的状态,在Windows上也可以实现这一点

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

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


这实际上可能是我们能找到的最接近于正确解决方案的地方。如果有人能提供其他想法,我洗耳恭听,但我担心机会渺茫。

我不知道它在
colorama
方面会如何工作,但在对我在网上找到的几个
tee
Windows实用程序不满意之后,我最终用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)
“”“
将标准输入复制到指定文件的标准输出(屏幕)*和*。
"""
导入文件输入
导入操作系统
从pathlib导入路径
导入系统
显示完整路径=错误
除法器=真
DIV_CH=“”
如果len(sys.argv)!=2:
raise SystemExit('用法:mytee')
尝试:
inp=fileinput.input(())#从标准输入读取。
路径=路径(sys.argv[1])
stdout_write=sys.stdout.write
stdout\u flush=sys.stdout.flush
#假定.py与输出文件位于同一目录中。
脚本=(f'{path.parent/path.stem}{path.suffix}',如果SHOW_FULL_path else
f'{path.stem}{path.suffix}')
将open(路径“w”)作为输出:#写入指定文件。
outp_write=outp.write
outp_flush=outp.flush
def写入(行):
标准输出写入(行)
输出写入(行)
def writeln(行):
写入(第+'\n'行)
banner=f'{script}'(如果脚本else中的''-[{script}]-'
writeln(f'{banner}')
如果分隔器:
书写(f'{DIV_CH*len(banner)}')
对于inp中的行:
写(行)
如果分隔器:
书写(f'{DIV_CH*len(banner)}')
writeln('-[done]-')
最后:
inp.close()#不确定这是否真的有必要。
系统出口(0)

这是个很酷的主意!从我用你的代码进行的测试中,它似乎也去除了颜色信息。很可能colorama在打印时会消耗颜色信息,因此零件永远不会传递给stout。也许我可以在python中找到另一种方法来捕获这些数据。谢谢你的主意!Windows的管道命令,
|
,可能是剥离颜色信息的原因。根据您的解决方案,可能有一种方法。init()具有可选属性“strip”。如果设置为False,它将保留控制字符。因此,如果我在代码中添加一个colorama.init(),并在我的代码中用colorama.init(strip=False)替换colorama.init(),那么它就可以工作了。这仍然不理想,因为我必须修改代码以适应变化,但这是朝着正确方向迈出的一步。塞尔诺:很高兴听到我的“一次性”实用程序被证明对其他人有用。请随意发布您自己的答案,其中包含支持
colorama
的更改,以供其他人使用。简单地问一个问题:您假设
tee
剥离ANSI序列,但您实际测试过吗
colorama
本身是终端感知的:“如果在Windows上或如果输出被重定向(不是tty),则默认行为是剥离。”@usr2564301。也许我说得不对。我认为
tee
不会剥离ANSI序列,但事实上,colorama确实如此。见我对马蒂诺回答的评论。我本想把这些信息整合到原来的帖子中,但还没有开始。解决方案可能是使用colorama的
strip
argum
@echo off
python teetest.py 2>&1 | tee log.txt
pause
Test
2nd Test
Color Test
Error_Test
"""
    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)