程序输出出现了转义序列作为打印字符。 如果我通过变量或管道将该字符串提供给PowerShell,则输出将按预期显示(红色文本)。
如何实现程序打印彩色文本而不需要任何解决方法?
这是我的程序源代码(Haskell)- 但语言不重要,只是为了让你看到转义序列的写法。
main = do
let red = "\ESC[31m"
let reset = "\ESC[39m"
putStrLn $ red ++ "RED" ++ reset
这是我的程序源代码(Haskell)- 但语言不重要,只是为了让你看到转义序列的写法。
main = do
let red = "\ESC[31m"
let reset = "\ESC[39m"
putStrLn $ red ++ "RED" ++ reset
注意:
以下内容适用于 Windows 上的常规控制台窗口(由 conhost.exe
提供),这是默认情况下使用的,包括从 GUI 应用程序启动控制台应用程序时。
相比之下,由Windows Terminal提供的控制台窗口(终端)以及Visual Studio Code 的集成终端默认提供对 VT / ANSI 转义序列的支持,适用于所有控制台应用程序。
虽然在 Windows 10 中,控制台窗口原则上支持 VT(虚拟终端)/ ANSI 转义序列,但默认情况下不支持。
您有三个选项:
(a) 通过注册表详细说明在this SU answer中,默认情况下全局启用支持并进行持久化。
[HKEY_CURRENT_USER\Console]
中,创建或设置VirtualTerminalLevel
DWORD值为1
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中:
PowerShell(Core)7+:将外部程序调用括在(...)
中(不可避免地先收集所有输出再打印):
(.\test.exe)
流式传输仅适用于Windows PowerShell的替代方法:将外部程序的输出导出到Write-Host
.\test.exe | Out-Host
请参阅下面的详细信息。
基于注册表的方法无论何时都会全局激活 VT 支持,即对所有控制台窗口生效,不管其中运行的是什么 shell / 程序:
单个可执行文件 / shell 仍然可以使用方法 (b) 取消支持。
相反,这意味着任何没有明确控制 VT 支持的程序输出都将受到 VT 序列的解释;虽然这通常是可取的,但假设某些程序意外地生成了带有 VT 类似序列的输出,则可能导致输出被错误解释。
注意:
虽然存在一种机制可以通过启动可执行文件 / 窗口标题的 子键 来定位控制台窗口设置,该机制是通过 [HKEY_CURRENT_USR\Console]
实现的,但似乎不支持 VirtualTerminalLevel
值。
即使有,它也不是一个 强大的 解决方案,因为通过 快捷方式 文件 (*.lnk
)(例如从“开始”菜单或任务栏),打开控制台窗口将不会遵守这些设置,因为 *.lnk
文件内置了一些设置;虽然可以通过 属性
GUI 对话框修改这些内置设置,但截至本文撰写时,VirtualTerminalLevel
设置并未在 GUI 中显示。
在程序(进程)内部调用SetConsoleMode()
Windows API函数,如此处所示,即使在C#中也很麻烦(因为需要P/Invoke声明),并且可能不是一个选项:
对于不支持调用Windows API的语言编写的程序。
如果您有一个无法修改的预先存在的可执行文件。
在这种情况下,接下来讨论的选项(c)(来自PowerShell)可能适合您。
PowerShell在启动时会自动激活VT(虚拟终端)支持本身(在最近的Windows 10版本中,这适用于Windows PowerShell和PowerShell(Core)7+),但是在v7.3.2版中,这并不适用于从PowerShell中调用的外部程序,无论是哪个版本。
$PSStyle.OutputRendering
首选项变量,它控制PowerShell命令是否通过格式化系统生成彩色输出,例如Get-ChildItem
输出的彩色标题。但是,此设置对于外部程序的(直接)输出没有影响。$PSStyle.OutputRendering
默认为Host
,这意味着只有打印到终端(控制台)的格式化输出才会被着色。$PSStyle.OutputRendering = 'PlainText'
禁用了着色,$PSStyle.OutputRendering = 'Ansi'
则使其无条件;请参见this answer获取更多信息。然而,作为一种解决方法,您可以通过PowerShell的(成功)输出流中继外部程序的(stdout)输出,这样VT序列将会被识别:
截至PowerShell(Core) 7.3.2,这只能通过将调用括在(...)
中或使用Out-String
来实现,但请注意,在打印之前,所有输出都会被收集。[1]
(.\test.exe)
在Windows PowerShell中,除了上述方法外,还可以通过管道传输到Write-Host
(Out-Host
、Write-Output
或Out-String -Stream
也可以)来进行流式传输。
.\test.exe | Write-Host
注意:只有当您想要打印到控制台时才需要使用这些技术。相反,如果您想要捕获外部程序的输出(包括转义序列),请使用$capturedOutput = .\test.exe
字符编码注意事项:Windows PowerShell默认期望外部程序的输出使用OEM代码页,由遗留系统区域设置(例如,在美国英语系统上为437
)定义,并反映在[console]::OutputEncoding
中。
.NET控制台程序自动遵守该设置,但对于使用不同编码(并且产生不仅仅是7位ASCII输出的非.NET程序(例如Python脚本)),您必须(至少暂时)通过分配给[console]::OutputEncoding
来指定该编码;例如,对于UTF-8:
[console]::OutputEncoding = [Text.Encoding]::Utf8
.
请注意,这不仅对于VT序列解决方法是必要的,而且通常对于PowerShell正确解释非ASCII字符是必要的。
PowerShell Core(v6+)很不幸,截至v7.3.2,仍然默认使用OEM代码页,但这应该被视为一个错误(请参见GitHub问题#7233),因为它否则默认为UTF-8 without BOM。
[1] 使用Out-String -Stream
或其内置的包装函数oss
来实现流式输出是很诱人的,但自PowerShell 7.3.2起,这不再起作用,可能是由于在GitHub PR #16612中实施的优化所致。
HKCU:\Console - [DWORD] VirtualTerminalLevel = 1
重新启动控制台,它就能正常工作了。
SetConsoleMode
方法来将控制台模式更改为ENABLE_VIRTUAL_TERMINAL_PROCESSING
(0x0004
)。但对我来说没有起作用。kernel32.SetConsoleMode(kernel32.GetStdHandle(-11), 7)
7
而不是4
(即来自MSDN的ENABLE_VIRTUAL_TERMINAL_PROCESSING
),您就可以使用ANSI序列。
os.system('color')
参考:https://dev59.com/U3VC5IYBdhLWcg3wcw0m - YehlaANSI
。 - Robert LoveANSI
吗?此答案讨论了对ANSI / VT转义序列的支持,这些序列也在Windows PowerShell和早期PowerShell(Core)版本中得到支持。但是,只有最近的PowerShell(Core)版本才将该支持扩展到_外部程序_。请注意,在v7.2+中,有$PSStyle.OutputRendering
来控制PowerShell自己命令的着色行为;可以将此变量设置为Ansi
,但其默认值为Host
。我已更新答案以澄清$PSStyle.OutputRendering
的作用。 - mklement0$PSStyle.OutputRendering
(在启动了-NoProfile
的会话中,以确保安全)。我已经提出了一个问题来纠正这个问题:https://github.com/MicrosoftDocs/PowerShell-Docs/issues/9497 - mklement0