Preserve color from piped output
Asked Answered
D

3

5

I have this extremely simple powershell script that prepends each line with a number.

function number
{    
    $i = 0;
    foreach($line in $input)
    {
        [string]$i + ":" + $line
        ++$i
    }
}

I would like this function to preserve the colors of the input. For example if I execute the following command the colors are lost:

git status -s | number

While executing

git status -s

Gives me a nicely colored output. What can I change in my powershell function to accomplish this.

Note I've already tried telling git to always output colors with the information in the answers to this question so I think its my side that is ignoring the colors.

This is not a duplicate of the question this one was previously marked a duplicate of.

I've seen that question, which is actually about how to prevent MSBuild from writing to standard error instead of standard out, which prevented the colors from showing up in that case. The only thing similar about that question and this one is the title. The other answer there (with HTML conversion) does not apply here as that requires the data is written to the console and not piped.

Desalvo answered 31/8, 2016 at 9:7 Comment(0)
H
4

As far as I'm aware, you can't preserve the colors output by git status -s, or any other command output that is piped into PowerShell via StdIn. The color information of the text being piped into the PowerShell function via StdIn is 'lost'.

The only way I can think of adding color back in would be to perform RegEx matches or positional based coloring, using multiple Write-Host "line section" -ForegroundColor COLOR -NoNewline for each colored section.

The console uses System.IO.StreamReader to accept input via StdIn. Try this command [Console]::In | Get-Member in PowerShell/ISE.

Housley answered 31/8, 2016 at 14:49 Comment(2)
Unfortunately that seems the case. Too bad I came across some answers for other shells (bash) in which its possible.Desalvo
The regex-based coloring is interesting, but note that PowerShell does pass color codes through, you just need to make sure the input command emits them even with its stdout connected to a pipeline, which usually requires a - utility-specific - option, such as -c color.status=always in the case of Roy's command.Windbreak
W
5
  • Utilities (command-line programs) that support colorized output usually omit color codes when their stdout is not connected to a terminal (console), such as when output is redirected to a file or sent through the pipeline.

    • The idea is not to pollute the data with formatting instructions if the intent is programmatic processing of the output (as opposed to display in the terminal).
  • In that event you must use a utility-specific option, IF supported in order to force unconditional use of coloring.

PowerShell does pass color codes through the pipeline, so something like the following, executed from a directory with a git repository, should produce colored output.

PS> git -c color.status=always status -b --short | % { $i=0 } { "$((++$i)): $_" }
1: ## master

The word master (or whatever branch is active) should appear in green.

The answer from which the -c color.status=always technique is taken was updated since you linked to it, so I suspect that an earlier form was simply not effective in forcing unconditional colored output.

Note:

  • For programs that use conditional coloring without providing an override via a parameter that makes it unconditional (such as -c color.status=always for Git or --color=always for GNU utilities), a workaround is possible in Unix-like environments (including WSL on Windows) via the (de facto) standard script utility, as demonstrated in this answer.
Windbreak answered 19/3, 2018 at 20:13 Comment(2)
great answer as always mr Klement! is there a simple way to "fake" a terminal, for apps that don't have an argument like --color=always? i'm using Tee-Object with the -Variable xxx argument, but almost every app i run this way has no color this way..Campbellite
Thanks, @aetonsi. I can only think of a solution for Unix-like environments (including WSL on Windows), using the script utility; e.g.: (script -qc 'ls --color=auto /mnt/c/windows' /dev/null) | Tee-Object -Variable out (--color=auto is used to trigger coloring that is normally suppressed in the pipeline).Windbreak
H
4

As far as I'm aware, you can't preserve the colors output by git status -s, or any other command output that is piped into PowerShell via StdIn. The color information of the text being piped into the PowerShell function via StdIn is 'lost'.

The only way I can think of adding color back in would be to perform RegEx matches or positional based coloring, using multiple Write-Host "line section" -ForegroundColor COLOR -NoNewline for each colored section.

The console uses System.IO.StreamReader to accept input via StdIn. Try this command [Console]::In | Get-Member in PowerShell/ISE.

Housley answered 31/8, 2016 at 14:49 Comment(2)
Unfortunately that seems the case. Too bad I came across some answers for other shells (bash) in which its possible.Desalvo
The regex-based coloring is interesting, but note that PowerShell does pass color codes through, you just need to make sure the input command emits them even with its stdout connected to a pipeline, which usually requires a - utility-specific - option, such as -c color.status=always in the case of Roy's command.Windbreak
S
0

If you are writing the o/p to the console[Host - to be precise]. You can use Write-Host cmdlet, which provides a "foreground" and "background" property, that you can use to set the colors of your choice.

Synclastic answered 31/8, 2016 at 10:32 Comment(1)
That only lets me set my own colours, not re-use those outputted by GitDesalvo

© 2022 - 2024 — McMap. All rights reserved.