PowerShell控制台中使用ANSI /VT 100代码的彩色文本输出

q43xntqr  于 2022-12-18  发布在  Shell
关注(0)|答案(3)|浏览(214)

我写了一个打印字符串的程序,其中包含ANSI escape sequences来使文本着色。但是它在默认的Windows 10控制台中并不像预期的那样工作,就像你在截图中看到的那样。
程序输出显示转义序列作为打印字符。如果我通过变量或管道将该字符串提供给PowerShell,输出将按预期显示(红色文本)。
我怎样才能实现该程序打印彩色文本没有任何变通办法?

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

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

oxcyiej71#

注:

  • 以下内容适用于Windows(由conhost.exe提供)上的 * 常规 *(传统)控制台窗口,这些窗口 * 默认情况下 * 使用,包括从GUI应用程序启动控制台应用程序时。
  • 相比之下,Windows Terminal提供的控制台窗口(终端)* 默认情况下 * 为 * 所有 * 控制台应用程序提供VT / ANSI转义序列支持。

虽然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

  • 打开新的控制台窗口以使更改生效。
  • 请参见下面的警告。
  • (B)通过调用SetConsoleMode() Windows API函数,从程序内部激活支持 ,仅针对该程序(进程)
  • 详情见下文。
  • (c)临时变通办法, 来自PowerShell**:将外部程序的输出传输到Out-Host;例如,.\test.exe | Out-Host
  • 详情见下文。

回复(a):

基于注册表的方法总是 * 全局地 * 激活VT支持,即对于 * 所有 * 控制台窗口,而不管在其中运行什么 shell /程序:

  • 如果需要,单个可执行文件/ shell 仍然可以使用方法(b)停用对它们自己的支持。
  • 然而,相反地,这意味着不明确控制VT支持的任何程序的输出将服从VT序列的解释;虽然这通常是期望的,但是假设这可能导致对来自“偶然地”产生具有VT类序列的输出的程序的输出的误解。

注:

  • 虽然有一种机制允许控制台窗口设置通过[HKEY_CURRENT_USR\Console]subkeys 由启动可执行文件/窗口标题限定范围,但VirtualTerminalLevel值似乎在那里不受支持。
  • 然而,即使是这样,它也不是一个 * 健壮 * 的解决方案,因为通过 * 快捷方式 * 文件(*.lnk)(例如从开始菜单或任务栏)打开控制台窗口不会遵守这些设置,因为*.lnk文件 * 内置了设置 *;虽然您可以通过Properties GUI对话框修改这些内置设置,但在撰写本文时,VirtualTerminalLevel设置尚未出现在该GUI中。

回复(B):

从程序(进程)* 内部调用SetConsoleMode() Windows API函数 *(如下所示),即使在C#中也很麻烦(因为需要P/Invoke声明),而且可能不是选项

  • 对于使用不支持调用Windows API的语言编写的程序。
  • 如果您已经存在无法修改可执行文件。

在这种情况下,下面讨论的选项(c)(来自PowerShell)可能对您有用。

回复(c):

PowerShell在启动时*会自动为自身激活VT(虚拟终端)支持(在Windows 10的最新版本中,这同时适用于Windows PowerShell和PowerShell(Core)7+),但不会 * 扩展到从 * Windows PowerShell调用的外部程序。但从PowerShell(Core)调用时,*会 * 扩展到至少从7.2起的版本。

  • 另外,在v7.2+中有**$PSStyle.OutputRendering * 首选项变量***,它**控制 PowerShell 命令是否通过formatting system**生成彩色输出, 例如Get-ChildItem输出的彩色标题。从 * 外部程序 * 的(直接)输出。$PSStyle.OutputRendering默认为Host,这意味着只有打印到终端(控制台)的格式化输出是彩色的。$PSStyle.OutputRendering = 'PlainText'禁用彩色,$PSStyle.OutputRendering = 'Ansi'使其无条件;有关详细信息,请参见this answer

但是,作为Windows PowerShell变通方案,您可以通过PowerShell命令中继 * 外部程序的输出,在这种情况下,* VT序列 * 被 * 识别**;使用Out-Host是最简单的方法(Write-Host也可以):

.\test.exe | Out-Host

注:仅当您打算打印到 * 控制台 * 时才使用Out-Host;相反,如果您想 * 捕获 * 外部程序的输出,则只使用$capturedOutput = .\test.exe
或者,您可以**将调用封装在(...)*中,但是,在中继之前, 总是先收集所有输出 *。

(.\test.exe)

字符编码警告:默认情况下,Windows PowerShell希望外部程序的输出使用OEM代码页,如旧系统区域设置所定义(例如,美国-英语系统上的437)和[console]::OutputEncoding中所反映的。.NET控制台程序会自动遵守该设置,但非.NET程序除外(例如Python脚本)使用不同的编码(并且不只是生成纯ASCII输出(在7位范围内)),则必须(至少暂时)通过赋值[console]::OutputEncoding来指定该编码;例如,对于UTF-8:

[console]::OutputEncoding = [Text.Encoding]::Utf8 .

请注意,这不仅是VT序列解决方案所必需的,而且通常是PowerShell正确解释非ASCII字符所必需的
PowerShell Core(v6+),不幸的是,从v7.3开始,仍然默认为OEM代码页,但是should be considered a bug,因为它默认为 UTF-8,没有BOM

epfja78i

epfja78i2#

感谢用户mklement0让我意识到VT支持不是自动启用的,这让我看向了正确的方向,我找到了this helpful post
所以要回答我的问题:添加或设置注册表项

HKCU:\Console  -  [DWORD] VirtualTerminalLevel = 1

重新启动控制台,它就能工作了。

pw136qt2

pw136qt23#

特别是对于Haskell,您需要调用hSupportsANSIWithoutEmulation函数以在Windows 10上的程序中启用ANSI转义序列。

相关问题