Python 子进程,使用颜色实时打印并保存标准输出

Python subprocess, realtime print with COLORS and save stdout

在保存结果的同时打印子进程的输出并不是一个新问题,之前已经回答过很多次,例如: 这对我不起作用,因为我试图保持打印的 shell 颜色。例如。当出现 systemctl status application 时,它会以绿色打印 运行。 上述方法都是从subprocess中一行一行的读取,但是在我看来颜色信息被剥离掉了。

我试图制作一个对象,它不在标准输出打印中,并将它们保存到一个变量中:

from subprocess import *
import sys

class Tee():
    def __init__(self):
        self.content = ''
        self.stdout = sys.stdout
        sys.stdout = self
    def __enter__(self):
        return self
    def __exit__(self, *args):
        pass
    def __del__(self):
        sys.stdout = self.stdout
    def write(self, data):
        self.content += data
        self.stdout.write(data)
    def flush(self):
        self.content = ''

with Tee() as tee:
    # Saves print to tee.content
    print("Hello World")

    # This line does not save prints to tee.content    
    run(['apt-get', 'update'])

    # raises an error that tee.fileno is not supported
    run(['systemctl', 'status', 'nginx'], stdout=tee)

    content = tee.content

print("---------------------")
print(content)

但问题是子进程的标准输出需要一个实际文件:

有没有办法实时打印子进程的输出,同时保持颜色,并将值存储到变量(不通过临时文件)?

systemctl 检查输出的去向;如果是 tty,它会显示彩色输出。如果 STDOUT 未连接到 tty,则不会显示颜色。

所以,基本上你需要从源代码中做到这一点,即当 STDOUT 不是 tty 时,systemctl 发出必要的转义码。

有办法,来自man systemd

$SYSTEMD_COLORS
    Controls whether colorized output should be generated.

所以你需要传递 SYSTEMD_COLORS 环境变量来使 systmectl return 输出颜色转义。

你可以这样做:

os.environ['SYSTEMD_COLORS'] = '1'
subprocess.run(['systemctl', 'status', 'application'], stdout=subprocess.PIPE)

要让此命令的环境变量仅在之后执行 del os.environ['SYSTEMD_COLORS']

或 运行 on shell 直接用于单个命令:

subprocess.run('SYSTEMD_COLORS=1 systemctl status application', shell=True, stdout=subprocess.PIPE)

您无法使用 subprocess,但 pty 可以。 pty 创建伪终端,因此正在执行的命令检测到它是 运行 tty 并启用彩色输出。

import pty, os

output_bytes = []

def read(fd):
    data = os.read(fd, 1024)
    output_bytes.append(data)
    return data

pty.spawn([command], read)
output = str(output_bytes)
# parse output as you need