PowerShell 7.2 introduced the automatic variable $PSStyle for a new feature called PSAnsiRendering. In this post, I show you how you can use ANSI rendering to control text decorations, such as color and font styling, in PowerShell.
Latest posts by Jeffery Hicks (see all)

For some time now, we've been able to use ANSI escape sequences in a PowerShell session. You've seen this when tying a command at a console prompt. Commands, parameters, strings, and operators are displayed in color. These colors are defined as PSReadline options.

PSReadline Options

PSReadline Options

The ANSI sequences begin with an escape, which is [Char]0x1b or [Char]27. In PowerShell 7, we can use `e. The ANSI sequence follows the escape. This entire sequence precedes the string you wish to treat. You typically close the sequence with a reset command, $([char]27)[0m.

Creating an ANSI string

Creating an ANSI string

You can also use ANSI to bold or underline.

Underline with ANSI formatting

Underline with ANSI formatting

Not only is this fun, but it can also add value to PowerShell output. To make this easier to use, Microsoft introduced a new feature in PowerShell 7.2 called PSStyle.

$PSStyle ^

As far as I can see, there are no cmdlets for this feature. Everything you need is wrapped up in a new variable called $PSStyle.

The PSStyle variable

The PSStyle variable

The ANSI values are shown in their rendered colors.

Important

It is very important to note that if you are running PowerShell in Windows Terminal, you will get different interpretations based on your color scheme.

Use these values in place of the actual ANSI sequence.

"`nRunning $($psstyle.Background.Blue)PowerShell$($psstyle.Reset)$($psstyle.Reset) $($psstyle.Foreground.Yellow)v{0}$($psstyle.Reset) on $($psstyle.Foreground.BrightCyan){1}$($psstyle.Reset)." -f $($psversiontable.PSVersion),(Get-CimInstance win32_operatingsystem).caption

I may have gone a bit overboard here, and I think there is a legitimate debate as to whether this is easier than using the actual ANSI sequences. But it works.

A PSStyle formatted string

A PSStyle formatted string

You might use this to color part of your prompt. Or maybe to add emphasis to a warning message.

$msg = "Could not find part of path: {0}{1}{2}{3}" -f $psstyle.Italic,$psstyle.Background.Cyan,"c:\work",$psstyle.Reset
Write-Warning $msg

In this example, I'm combining styles.

PSStyle formatted warning

PSStyle formatted warning

Customizing style ^

The foreground and background color values are read-only. However, the $PSStyle object has a method you can use to set a foreground or background color from a set of RGB values.

"$($PSStyle.Foreground.FromRgb(0,200,100))I am the walrus$($PSStyle.Reset)"
PSStyle RGB

PSStyle RGB

I'm more likely to create and assign the new value to a variable.

$custom = $PSStyle.Foreground.FromRgb(0,200,100)
"$($custom)I am the walrus$($PSStyle.Reset)"

But there's much more to PSStyle.

Format rendering ^

In PowerShell 7.2, you'll notice that formatted tables have a little style.

Formatted table with style

Formatted table with style

Where did those green column headings come from?

PSStyle formatting

PSStyle formatting

You can modify these values instead of changing values in $host.privatedata, as we did in previous versions of PowerShell. I'll change the error color.

$PSStyle.Formatting.Error = "`e[38;5;201m"
PSStyle error format

PSStyle error format

You'll notice that the error message is displayed in red, which is the value in $host.privatedata. However, when I look at the error with Get-Error, the formatted result uses my new PSStyle setting.

Here's a change to warnings.

PSStyle warning format

PSStyle warning format

Or change the table headers.

$psstyle.Formatting.TableHeader = "`e[3;38;5;195m"
Custom PSStyle table header

Custom PSStyle table header

If you want to revert to plain-text tables, you can change the rendering setting.

$psstyle.OutputRendering = "Plaintext"

However, this will disable all ANSI-related settings. To change it back, set the value to "ANSI".

Be aware that any changes to $PSStyle are not persistent unless you add them to your PowerShell profile script.

By the way, if you are wondering where I got the ANSI sequences, I used the Show-ANSISequence command from the PSScriptTools module, which you can install from the PowerShell Gallery.

Progress style ^

One of the big changes in PowerShell 7.2 is how progress bars are displayed. The new format is more Linux-like and command line-friendly. It also doesn't add as much overhead as the previous progress displays. Settings for the new progress style are configured here.

PSStyle progress settings

PSStyle progress settings

Now, when you run a command that uses Write-Progress, you'll get something like this.

PSStyle ProgressBar

PSStyle ProgressBar

The command above is also from the PSScriptTools module.

If you need or prefer the legacy look, you can easily set that.

$psstyle.Progress.view = "Classic"

Use a value of 'Minimal' for the new style. Or feel free to adjust the color.

$psstyle.Progress.Style = "`e[38;5;123m"

Here's some silly sample code to see this in action.

1..100 | ForEach-Object -Begin {$i= 0} -process { 
    $i++
    $p = ($i/100)*100
    Write-progress -Activity "Demo" -CurrentOperation "Processing $_" -Status "Square = $($_*$_)" -PercentComplete $p
    Start-Sleep -Milliseconds 100
}
Custom PSStyle ProgressBar

Custom PSStyle ProgressBar

One potential drawback of this new style is that it appears that the CurrentOperation line is left out.

File Info ^

I've saved the best part for last. You can use $PSStyle to format directory listings.

Default PSStyle FileInfo settings

Default PSStyle FileInfo settings

However, if you do a directory listing, you probably won't recognize any difference. To take advantage of this feature, you need to enable an experimental feature.

Enable-ExperimentalFeature PSANSIRenderingFileInfo

You'll see a message about restarting PowerShell. Unlike changes to values in $PSStyle, this change is persistent until you disable the feature. But look at the difference.

ANSI file output

ANSI file output

It might be hard to detect the color of PS1 files. No problem. I can change that.

$psstyle.FileInfo.Extension[".ps1"] = "`e[38;5;123m"
Custom ANSI FileInfo

Custom ANSI FileInfo

You can't change what Windows detects as an executable here, but you can add a file extension.

$PSStyle.FileInfo.Extension.add(".xml","`e[38;5;190m")
Custom file formatting

Custom file formatting

It isn't that difficult to define a style for a group of file extensions.

$ext =".gif",".bmp",".png",".jpg",".jpeg"
foreach ($e in $ext) {
    $PSStyle.FileInfo.Extension.add($e,"`e[38;5;111m")
}

Since I've been having so much fun with this, I realized I needed a good way to configure my FileInfo settings without having to constantly be editing a profile script. I wrote a set of PowerShell functions, one to export FileInfo settings to a JSON file and another to import them. You can find the functions online here. Save the PS1 file to your desktop, and dot source it.

Once you've configured your FileInfo settings, export them to a JSON file.

Export-PSStyleFileInfo C:\work\fileinfo.json

In your PowerShell profile script, dot source the PS1 file and then import the settings.

Import-PSStyleFileInfo c:\work\fileinfo.json

Summary ^

I'm very excited to see how I can incorporate PSStyle into my everyday PowerShell work and scripting. Remember, this requires PowerShell 7.2, so if you haven't updated, this might be a good reason to do so. And if you find a great use case for these settings, I'd love to hear about it.

avatar
4 Comments
  1. Alexander 8 months ago

    As always very detailed and interestingly written. Thanks a lot!

  2. thorsten reichelt 8 months ago

    I found out that using an array with |out-file causes undesirable characters in the file which when used by another script will break your script. I had posted this issue on github and the guys were nice enough to let me know about wrapping that statement in the $psstyle.outputrendering command.
    I tried using the $psstyle.OutputRendering = “Plaintext” at the begining of my script to avoid having to wrap every statement that wants to output an array to a file. seems to be working so far.

  3. Klaas 8 months ago

    Hi Jeff

    Thank you for this article and the PSStyle functions.

    For the directory listings I’ve been using Get-ChildItemColor and also Terminal-Icons, which adds icons. Both of those work in WindowsPowerShell as well.

    I’m a little concerned modules will start fighting eachother and that it becomes harder and harder to find where a setting or an issue comes from.
    What’s your advice on keeping this all manageable? Do you uninstall potential conflicting modules?

    • Author
      Jeff Hicks 8 months ago

      Hopefully, the module name makes it clear what it might do. You might import the modules in your PowerShell profile script and include a comment about what the module or command will be doing. How you manage conflicts might depend on whether it is something that is part of your interactive session or when running a script. In a script, you could always test for the conflicting module, remove it from the session, run your code, then re-import it. Or maybe the conflict is an indication that you don’t need both modules, and one has to go. There is no straight and straightforward answer to this question.

Leave a reply

Your email address will not be published.

*

© 4sysops 2006 - 2022

CONTACT US

Please ask IT administration questions in the forums. Any other messages are welcome.

Sending

Log in with your credentials

or    

Forgot your details?

Create Account