Description
User scenario
Since we've added more ANSI escape sequences for coloring in PS7, if you pipe or redirect that text output, the embedded ANSI escape sequences are sent down the pipeline. This can make log files harder to read. Also, for those wanting to leverage ANSI escape sequences, they are difficult to create/read for scripters and also harder to use formatting presets rather than hardcoding specific colors.
Fundamental philosophy
On Linux, if you use ls --color
then different file types use ANSI escape sequences as color indicators. If you pipe this output to less
, then you get paging while retaining the color information. If you redirect this output to a file, that file contains the ANSI escape sequences. If you then use the cat
command on the file, you see the coloring as the ANSI escape sequences are rendered by the terminal.
On macOS, the similar command is ls -G
, however, piping this output to less
or redirecting to a file you lose the ANSI escape sequences so it is just plain text. I believe ls
on macOS is detecting if output is redirected and turning off coloring automatically.
So for PowerShell, we should have a consistent experience regardless if Windows, Linux, or macOS and interop with native commands that may output ANSI escape sequences in addition to cmdlets/formatting.
Since ANSI escape sequences are de facto standardized, we should not introduce a new intermediate format and should just embed ANSI escape sequences within strings. This will ensure compatibility with other tools that emit or handle ANSI escape sequences.
Proposed technical implementation details
A new automatic variable $PSStyle
will be added:
TypeName: System.Management.Automation.PSStyle
Name MemberType Definition
---- ---------- ----------
Reset Property string AttributesOff {get;set;}
Background Property System.Management.Automation.PSStyle+BackgroundColor Background {get;set;}
Blink Property string Blink {get;set;}
BlinkOff Property string BlinkOff {get;set;}
Bold Property string Bold {get;set;}
BoldOff Property string BoldOff {get;set;}
Foreground Property System.Management.Automation.PSStyle+ForegroundColor Foreground {get;set;}
Formatting Property System.Management.Automation.PSStyle+FormattingData Formatting {get;set;}
Hidden Property string Hidden {get;set;}
HiddenOff Property string HiddenOff {get;set;}
OutputRendering Property System.Management.Automation.OutputRendering OutputRendering {get;set;}
Reverse Property string Reverse {get;set;}
ReverseOff Property string ReverseOff {get;set;}
Standout Property string Standout {get;set;}
StandoutOff Property string StandoutOff {get;set;}
Underline Property string Underlined {get;set;}
UnderlineOff Property string UnderlinedOff {get;set;}
$PSStyle
The base members return ANSI escape sequences mapped to their names. These are also settable so the user can change bold to underlined, for example. This makes it easier for scripters to author decorated strings with tab completion:
"$($PSStyle.Background.LightCyan)Power$($PSStyle.Underlined)$($PSStyle.Bold)Shell$($PSStyle.Reset)"
$PSStyle.OutputRendering
This is of type System.Management.Automation.OutputRendering
which is an enum with the values:
- Automatic This is the default. If the host supports VirtualTerminal, then ANSI is always passed as-is, otherwise plaintext
- ANSI ANSI is always passed through as-is
- PlainText ANSI escape sequences are always stripped so that it is only plain text
- HostOnly This would be the macOS behavior where redirected or piped output the ANSI escape sequences are removed
$PSStyle.Formatting
This effectively replaces $Host.PrivateData
as the way to read or configure colors for formatting rendering. $Host.PrivateData
will continue to exist for backwards compatibility, but is not connected to $PSStyle.Formatting
.
TypeName: System.Management.Automation.PSStyle+FormattingData
Name MemberType Definition
---- ---------- ----------
Debug Property string Debug {get;set;}
Error Property string Error {get;set;}
ErrorAccent Property string ErrorAccent {get;set;}
FormatAccent Property string FormatAccent {get;set;}
Verbose Property string Verbose {get;set;}
Warning Property string Warning {get;set;}
One difference here is that instead of being of type ConsoleColor
, these are all strings and doesn't separate foreground and background colors. This means that a single member can have foreground and background colors defined as well as other attributes like bold, underlined, etc...
Scripters can easily leverage this:
"$($PSStyle.Formatting.ErrorAccent)Power$($PSStyle.Formatting.Verbose)Shell$($PSStyle.AttributesOff)"
$PSStyle.Foreground and $PSStyle.Background
These members contain the standard 16 console colors as well as a RGB() methods to specify 24-bit color. For the colors, the values are settable and because they are strings can be any string content and any number of ANSI escape sequences.
TypeName: System.Management.Automation.PSStyle+ForegroundColor
Name MemberType Definition
---- ---------- ----------
Rgb Method string Rgb(byte red, byte green, byte blue), string Rgb(int rgb)
Black Property string Black {get;set;}
Blue Property string Blue {get;set;}
Cyan Property string Cyan {get;set;}
DarkGray Property string DarkGray {get;set;}
Green Property string Green {get;set;}
LightBlue Property string LightBlue {get;set;}
LightCyan Property string LightCyan {get;set;}
LightGray Property string LightGray {get;set;}
LightGreen Property string LightGreen {get;set;}
LightMagenta Property string LightMagenta {get;set;}
LightRed Property string LightRed {get;set;}
LightYellow Property string LightYellow {get;set;}
Magenta Property string Magenta {get;set;}
Red Property string Red {get;set;}
White Property string White {get;set;}
Yellow Property string Yellow {get;set;}
PowerShell engine changes
The formatting system, pipelining, and redirection will be updated to respect the value of $PSStyle.OutputRendering
.
StringDecorated type
This is an internal type to handle ANSI escaped strings.
Constructor
Single constructor taking a string as input.
bool IsDecorated
property
Returns if the string contains ANSI escape sequences based on if the string contains ESC or C1 CSI.
int Length
property
Returns the length of just the text content of the string sans ANSI escape sequences.
StringDecorated Substring(int contentLength)
method
Returns a substring starting at index 0 up to the contentLength for content that is not part of an ANSI escape sequence. This is needed for table formatting to truncate strings preserving ANSI escape sequences that don't take up printable character space.
string ToString()
method
Returns the plaintext version of the string.
string ToString(bool Ansi)
method
Returns the raw ANSI embedded string if Ansi
parameter is true, otherwise returns plain text with ANSI escape sequences removed.
Out-String
update
To enable scripts to easily allow plain text redirection, Out-String
will have a -RemoveAnsi
switch.
Select-String
update
By default, it would make sense for Select-String
to ignore ANSI escape sequences, perhaps a -IncludeAnsi
switch should be added.
$Host.PrivateData
This will be available for legacy scripts/modules that read from it. However, the engine changes (and console host changes) would mean these settings will no longer be observed and instead use the settings from $PSStyle
.
Alternate considerations
Original prototype was built on top of System.CommandLine.Rendering
, however, that is still a work in progress and is not targeted to be 1.0 in time for 7.1. It is also geared towards C# developers and the user experience is not great for scripts.