使用 ANSI/VT100 代码在 PowerShell 控制台中输出彩色文本

Colored text output in PowerShell console using ANSI / VT100 codes

我写了一个程序来打印一个字符串,其中包含 ANSI escape sequences 以使文本着色。但它在默认 Windows 10 控制台中没有按预期工作,如您在屏幕截图中所见。

程序输出以转义序列作为打印字符出现。 如果我通过变量或管道将该字符串提供给 PowerShell,输出将按预期显示(红色文本)。

如何在没有任何解决方法的情况下实现程序打印彩色文本?

这是我的程序源代码 (Haskell) - 但与语言无关,只是为了让您了解转义序列是如何编写的。

main = do
    let red = "\ESC[31m"
    let reset = "\ESC[39m"
    putStrLn $ red ++ "RED" ++ reset

虽然 console windows in Windows 10 支持 VT(虚拟终端)/ANSI 转义序列 原则上, 默认情况下关闭支持.

您有三个选择:

  • (a) 默认情况下全局激活支持,通过注册表持续,详见this SU answer.

    • 简而言之:在注册表项 [HKEY_CURRENT_USER\Console] 中,创建 VirtualTerminalLevel DWORD 值或将其设置为 1
      • 在 PowerShell 中,您可以以编程方式执行此操作,如下所示:
        Set-ItemProperty HKCU:\Console VirtualTerminalLevel -Type DWORD 1
      • 来自 cmd.exe(也适用于 PowerShell):
        reg add HKCU\Console /v VirtualTerminalLevel /t REG_DWORD /d 1
    • 打开一个新的控制台window以使更改生效。
    • 请参阅下面的注意事项。
  • (b) 从程序内部激活支持,仅针对该程序(进程),使用调用 SetConsoleMode() Windows API 函数。

    • 查看下面的详细信息。
  • (c) Ad-hoc 解决方法,来自 PowerShell:管道输出从外部程序到 Out-Host;例如,.\test.exe | Out-Host

    • 查看下面的详细信息。

回复 (一):

registry-based 方法 总是激活 VT 支持 全局 ,即 所有 控制台 windows,不管shell/程序在其中运行什么:

  • 单个可执行文件/shells 仍然可以停用对自己的支持,如果需要,使用方法 (b)。

  • 然而,相反,这意味着任何未明确控制 VT 支持的程序的输出都将受 VT 序列的解释;虽然这通常是可取的,但假设这可能会导致对 意外地 产生具有 VT-like 序列的输出的程序的输出的误解。

注:

  • 虽然一种允许console-window设置受启动可执行文件/window标题限制的机制,通过[= [HKEY_CURRENT_USR\Console] 的 89=]subkeys,那里似乎不支持 VirtualTerminalLevel 值。

  • 即使是,它也不是 可靠的 解决方案,因为通过 [=89] 打开控制台 window =]快捷方式 文件 (*.lnk)(例如从“开始”菜单或任务栏)不会遵守这些设置,因为 *.lnk 文件具有内置设置 ;虽然您可以通过 Properties GUI 对话框修改这些 built-in 设置,但在撰写本文时,VirtualTerminalLevel 设置并未出现在该 GUI 中。


回复 (b):

即使在 C# 中调用 SetConsoleMode() Windows API function from inside the program (process), as shown here 也很麻烦(由于需要 P/Invoke 声明),并且 可能不是一个选项:

  • 对于使用不支持调用 Windows API 的语言编写的程序。

  • 如果您已有无法修改的可执行文件。

在这种情况下,接下来讨论的选项 (c)(来自 PowerShell)可能适合您。


回复 (c):

PowerShell 在启动时自动激活 VT(虚拟终端)支持自身(在 Windows 10 的最新版本中这适用于 Windows PowerShell 和 PowerShell (Core) 7+) - 但是 确实 扩展到外部程序 Windows PowerShell(但至少从 7.2.2 开始从 PowerShell(核心)调用时 )。

但是,如果您通过 PowerShell 中继 外部程序的输出,VT 序列 识别;使用 Out-Host 是最简单的方法(Write-Host 也可以):

.\test.exe | Out-Host

注意:仅当您要打印到 控制台 时才使用 Out-Host;相反,如果您想 捕获 外部程序的输出,只需使用 $capturedOutput = .\test.exe

或者,您可以将调用包含在(...)中,但是,总是先收集所有输出,然后再转发它。

(.\test.exe)

Character-encoding 警告:Windows 默认情况下,PowerShell 希望外部程序的输出使用 OEM 代码页,如遗留系统区域设置(例如,437 在 US-English 系统上)并反映在 [console]::OutputEncoding 中。 .NET 控制台程序自动遵守该设置,但对于使用不同编码(并且不仅产生纯 ASCII 输出(在 7 位范围内))的非 .NET 程序(例如 Python 脚本),您必须(至少暂时)通过分配给 [console]::OutputEncoding 来指定该编码;例如,对于 UTF-8:
[console]::OutputEncoding = [Text.Encoding]::Utf8.
请注意,这不仅是 VT-sequences 解决方法所必需的,而且 PowerShell 通常也需要这样才能正确解释 non-ASCII 个字符 .

PowerShell Core (v6+), 不幸的是从 v7.2 开始,仍然默认为 OEM 代码页,但是 should be considered a bug,因为它默认为没有 BOM.

UTF-8

感谢用户 mklement0 让我知道 VT 支持不会自动启用。这让我看对了方向,我发现 this helpful post.

所以回答我的问题:添加或设置注册表项

HKCU:\Console  -  [DWORD] VirtualTerminalLevel = 1

重启控制台即可。

对于 Haskell 具体而言,您需要在 Windows 10.

上调用 hSupportsANSIWithoutEmulation 函数以在您的程序中启用 ANSI 转义序列