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

3

3

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?

Empyrean answered 16/5, 2022 at 18:26 Comment(5)
I'm not confident how it works in the PowerShell ecosystem, but in Unix-like systems, it's often the command itself that detects whether output is to a terminal, and switches off colour control codes.Stochmal
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.Nettles
depending on how much output you have, and what you're trying to do with it, maybe you can have the output assign a tag to itself. for example. [pscustomobject]@{type="error";output=$results} this way you can work with the object with a foreach loop and switch/if cases... and if you just want the output, you could do write-host $results.output you could even have a parameter switch that says kitchen create instance -colorTag which could enable the tagging feature but i'm not sure what language your kitchen function/command is written in. instead of type="error" you could do color="red"Eu
@Nettles I was afraid of this. I used TK as my example although that does have an option to provide unconditional colors. However, many utilities which provide color output don't, as you mentioned. I was hoping I might either be able to change the behavior during variable assignment at least, or write a module to assist with preserving console colors, to cover utilities which do not offer a force-color option.Empyrean
See also: PowerShell output with color redirected to fileBeneficiary
N
3

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)
Nettles answered 16/5, 2022 at 19:39 Comment(3)
Interestingly enough, I did find this answer on Super User. I don't know if script.exe is available standalone anywhere but that user reports it can be moved out of cygwin and run independently. Now, whether it handles all of the features the same way as in Unix is another story, but might be an avenue worth looking into.Empyrean
Thanks, @BendertheGreatest - if you're willing to explore this and report back, I'm happy to update the answer. Even if it works, though, having to install cygwin seems like a lot of effort.Nettles
If I ever explore this I will. I imagine if it's available in cygwin there perhaps is a consumable package (pacman, perhaps, if msys2 can install the same util) which the utility could be obtained from.Empyrean
I
2

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
Inopportune answered 12/4, 2023 at 3:37 Comment(2)
Got "The property 'OutputRending' cannot be found on this object. Verify that the property exists and can be set." on PS 7.3.6Jedthus
@EliO. Sorry that was an error on my part, I updated the answer to correct a spelling error in the property name.Inopportune
E
1

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.

Empyrean answered 16/5, 2022 at 18:53 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.