Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Colored text output in PowerShell console using ANSI / VT100 codes

People also ask

How do I change the text color in PowerShell?

You can specify the color of text by using the ForegroundColor parameter, and you can specify the background color by using the BackgroundColor parameter. The Separator parameter lets you specify a string to use to separate displayed objects.

Does PowerShell support ANSI codes?

PowerShell has many features that support the use of ANSI escape sequences to control the rendering of output in the terminal application that is hosting PowerShell. PowerShell 7.2 added a new automatic variable, $PSStyle , and changes to the PowerShell engine to support the output of ANSI-decorated text.


While console windows in Windows 10 do support VT (Virtual Terminal) / ANSI escape sequences in principle, support is turned OFF by default.

You have three options:

  • (a) Activate support globally by default, persistently, via the registry, as detailed in this SU answer.

    • In short: In registry key [HKEY_CURRENT_USER\Console], create or set the VirtualTerminalLevel DWORD value to 1
      • From PowerShell, you can do this programmatically as follows:
        Set-ItemProperty HKCU:\Console VirtualTerminalLevel -Type DWORD 1
      • From cmd.exe (also works from PowerShell):
        reg add HKCU\Console /v VirtualTerminalLevel /t REG_DWORD /d 1
    • Open a new console window for changes to take effect.
    • See caveats below.
  • (b) Activate support from inside your program, for that program (process) only, with a call to the SetConsoleMode() Windows API function.

    • See details below.
  • (c) Ad-hoc workaround, from PowerShell: pipe output from external programs to Out-Host; e.g., .\test.exe | Out-Host

    • See details below.

Re (a):

The registry-based approach invariably activates VT support globally, i.e., for all console windows, irrespective of what shell / program runs in them:

  • Individual executables / shells can still deactivate support for themselves, if desired, using method (b).

  • Conversely, however, this means that the output of any program that doesn't explicitly control VT support will be subject to interpretation of VT sequences; while this is generally desirable, hypothetically this could lead to misinterpretation of output from programs that accidentally produce output with VT-like sequences.

Note:

  • While there is a mechanism that allows console-window settings to be scoped by startup executable / window title, via subkeys of [HKEY_CURRENT_USR\Console], the VirtualTerminalLevel value seems not to be supported there.

  • Even if it were, however, it wouldn't be a robust solution, because opening a console window via a shortcut file (*.lnk) (e.g. from the Start Menu or Task Bar) wouldn't respect these settings, because *.lnk files have settings built into them; while you can modify these built-in settings via the Properties GUI dialog, as of this writing the VirtualTerminalLevel setting is not surfaced in that GUI.


Re (b):

Calling the SetConsoleMode() Windows API function from inside the program (process), as described here, is cumbersome even in C# (due to requiring a P/Invoke declaration), and may not be an option:

  • for programs written in languages from which calling the Windows API is not supported.

  • if you have a preexisting executable that you cannot modify.

If you don't want to enable support globally in that case (option (a)), option (c) (from PowerShell) may work for you.


Re (c):

PowerShell automatically activates VT (virtual terminal) support for itself when it starts (in recent releases of Windows 10 this applies to both Windows PowerShell and PowerShell Core) - but that does not extend to external programs called from PowerShell.

However, if you relay an external program's output via PowerShell, VT sequences are recognized; using Out-Host is the simplest way to do that (Write-Host would work too):

.\t.exe | Out-Host

Note: Use Out-Host only if you mean to print to the console; if, by contrast, you want to capture the external program's output, use just $capturedOutput = .\test.exe

Character-encoding caveat: Windows PowerShell by default expects output from external programs to use the OEM code page, as defined by the legacy system locale (e.g., 437 on US-English systems) and as reflected in [console]::OutputEncoding. .NET console programs respect that setting automatically, but for non-.NET programs (e.g., Python scripts) that use a different encoding (and produce not just pure ASCII output (in the 7-bit range)), you must (at least temporarily) specify that encoding by assigning to [console]::OutputEncoding; e.g., for UTF-8:
[console]::OutputEncoding = [Text.Encoding]::Utf8.
Note that this is not only necessary for the VT-sequences workaround, but generally necessary for PowerShell to interpret non-ASCII characters correctly.

PowerShell Core (v6+), unfortunately, as of v7.2, still defaults to the OEM code page too, but that should be considered a bug, given that it otherwise defaults to UTF-8 without BOM.


Thanks to user mklement0 for making me aware that VT support is not enabled automatically. This made me look in the right direction and I found this helpful post.

So to answer my question: Add or set the registry key

HKCU:\Console  -  [DWORD] VirtualTerminalLevel = 1

Restart the console and it works.


For Haskell specifically, you need to call the hSupportsANSIWithoutEmulation function to enable ANSI escape sequences in your program on Windows 10.