codewario
codewario

Reputation: 21518

Is there a way to preserve ANSI control codes in PowerShell command output?

In PowerShell, is there a way to preserve the ANSI control codes used to convey color information to the console when assigning a program's output to a variable?

For instance, I use Test Kitchen which provides colored output unique to each suite of tests to run. When I run kitchen create INSTANCE, I get output in several colors. However, if I assign the output to a variable, or pipe it to another cmdlet such as Tee-Object, that color information is lost. It seems that PowerShell strips this information out when the result is sent down the pipeline or assigned to a variable:

kitchen create INSTANCE           # Colored output

$output = kitchen create INSTANCE
Write-Host $output                # Color information is lost

Curiously though, I can implement control codes in my own strings and PowerShell is able to honor them when Virtual Terminal is enabled. These survive variable assignment, unlike command output:

$output = "`u{001b}[31mHello"
Write-Host $output                # Results in colored output

So it seems that the color information is being stripped out only from a program's output, and only if the value is assigned or sent down the pipeline. Is there a way to preserve these control codes from external commands?

Upvotes: 3

Views: 1372

Answers (3)

Ro Yo Mi
Ro Yo Mi

Reputation: 15010

I'm using this on PowerShell v7.3.3 and haven't tested on older versions of 7. I'm pretty sure this won't work for PowerShell 5 since that doesn't have $PsStyle

# Get the current setting so you can flip it back if needed
$OriginalValue = $PsStyle.OutputRendering

# this output should be color coded
$PsStyle.OutputRendering = "Ansi"
$String = Get-ChildItem | Select -First 3 | Out-String
Write-Host $String

# this output should not be color coded
$PsStyle.OutputRendering = "Host"
$String = Get-ChildItem | Select -First 3 | Out-String
Write-Host $String

# this should be back to your default/original behavior
$PsStyle.OutputRendering = $OriginalValue
$String = Get-ChildItem | Select -First 3 | Out-String
Write-Host $String

Upvotes: 2

mklement0
mklement0

Reputation: 440546

To add to your own answer:

  • On Windows I'm not aware of any simple solutions, though perhaps there is a programmatic way to do what the script utility does on Unix-like platforms (see next point).

  • On Unix-like platforms, you can use the script utility to make external programs believe they are connected to a terminal and thus make them produce colored output they would otherwise suppress:

    • script is not a POSIX-mandated utility, but it ships with at least some Linux distros, notably Ubuntu, as well as with macOS.
    • Notably, the macOS and Linux implementations use different syntax, as shown below.

Example:

  • Note:

    • ls --color=auto is used as a readily available test command, because it exhibits the conditional coloring behavior.

    • ls --color=always would exhibit unconditional coloring behavior, and would therefore obviate the need for script - as such, ls is an example of a utility that does allow you to request unconditional coloring; ls --color=auto merely serves as a stand-in for utilities that do not.

    • The variable assignment below ($out = ...) is enclosed in (...) in order to pass the value being assigned through, so you'll see right away that the coloring is preserved.

  • Linux

($out = script -qc 'ls --color=auto' /dev/null)
  • macOS:
($out = script -q /dev/null ls --color=auto)

Upvotes: 3

codewario
codewario

Reputation: 21518

As mentioned by @IMSoP and @mklement0 in the question comments, the problem does not lie within PowerShell. To quote @mklement0:

PowerShell is not to blame here. Most utilities (external programs) that are capable of colored output via ANSI escape sequences at least by default apply coloring selectively, namely only when stdout is connected to a terminal (i.e. when printing to the display). Such utilities may offer opt-in mechanisms to apply coloring unconditionally, via command-line options and/or environment variables.

Upvotes: 1

Related Questions