Color Coding with PowerShell

color-aspect-hero
Recently, I demonstrated how to bend the rules a bit in PowerShell to provide useful color coding for your PowerShell commands. Although I’d normally encourage you to think about objects in the pipeline, playing with text sometimes fits the bill and can even be a little fun. If you missed the previous articles, take a few minutes to get caught up.


I’ve been using PowerShell commands to check the status of critical services on my domain controllers. To further demonstrate what you can do, I’d like to be able to have services in statuses other than running or stopped. Since we’re dealing with text, I can be creative. I saved the results of my PowerShell command to get services to a text file and manually changed some of the statuses. I’ll bring that file into my PowerShell session.

$split = get-content C:\work\testservices.txt

A text array of service data (Image Credit: Jeff Hicks)
A text array of service data (Image Credit: Jeff Hicks)

This looks like regular PowerShell output, but it’s a collection of strings. I want to add a stop-light effect at the end of each line to indicate the service status.
Here’s my next solution:

foreach ($line in $split) {
#append some space after the line
$params=@{Object=" "}
Write-Host "$line " -NoNewline
#look at the line and add a parameter based on the results
#of a regular expression match
switch -Regex ($line) {
"Stopped" { $params.BackgroundColor = "Red" }
"Running" { $params.BackgroundColor = "DarkGreen" }
"Pending" { $params.BackgroundColor = "Magenta" }
"Paused" { $params.BackgroundColor = "Yellow" }
}
#write the line with the splatted parameters
Write-Host @params
} #close foreach

Services with a stop light indicator (Image Credit: Jeff Hicks)
Services with a stop light indicator (Image Credit: Jeff Hicks)

This technique is similar to what I showed you last time. I write each line using Write-Host, but I don’t include a new line. Next, I use the switch construct to examine each line. Notice that I’m using the –Regex option. This instructs PowerShell to perform a regular expression match between $line and each pattern, i.e., Stopped or Running.
I could have used any regular expression pattern, but I’m keeping it simple with plain text. When there’s a match, I add a parameter to the hashtable that will eventually be splatted to Write-Host, which is writing a single space. This process is repeated for each line until I get the result you see in the screenshot below.
Here’s a variation that highlights the entire line:

foreach ($line in $split) {
$params=@{Object=$line}
switch -Regex ($line) {
"Stopped" { $params.BackgroundColor = "Red"}
"Running" { $params.BackgroundColor = "DarkGreen"}
"Pending" { $params.BackgroundColor = "Magenta"}
"Paused" { $params.BackgroundColor = "Yellow"}
}
Write-Host @params
}

Highlighted lines (Image Credit: Jeff Hicks)
Highlighted lines (Image Credit: Jeff Hicks)

My color scheme might not be ideal, and you may only care to highlight specific statuses. That’s simple enough to adjust in the switch statement. How about another variation that uses regular expressions a bit more? I’ll create a regex object from the status enumerations.

[regex]$rx = ([enum]::GetNames([System.ServiceProcess.ServiceControllerStatus])) -join "|"

I’ll process each line, but only do fancy coloring if the line contains a status keyword based on a regex match. If it does, I’ll write the first part of the substring up to where the match starts. I can find that from the Index number in the match object.

A regex match (Image Credit: Jeff Hicks)
A regex match (Image Credit: Jeff Hicks)

Now, I’ll use a switch statement to do a regular expression match on the regex value to determine what color to use for the foreground. Then, I’ll write the matching value in that color. Here’s the code.

foreach ($line in $split) {
      $m = $rx.match($line)
      #only process if there is a match
    if ($m.success) {
      #get the point in the string where the match starts
      $i = $m.Index
      #display the line from start up to the match
      $line.Substring(0,$i) | write-host -NoNewline
      #select a foreground color based on matching status. Default should never be reached
    switch -Regex ($m.value) {
      "Stopped" { $fg = "Red" }
      "Running" { $fg = "Green" }
      "Pending" { $fg = "Magenta" }
      "Paused" { $fg = "Yellow" }
      Default { Write-Warning "Somehow there is an unexpected status" }
    }
      $line.substring($i) | Write-Host -ForegroundColor $fg
    }
    else {
      #just write the line as is if no match
      Write-Host $line
    }
} #close foreach

And here’s the result:

Colorized status results (Image Credit: Jeff Hicks)
Colorized status results (Image Credit: Jeff Hicks)


This code is written knowing that the status is at the end. In another article I’ll show you another way that is a bit more dynamic. But for now, let me leave you with one final variation that uses a hashtable of statuses and corresponding colors.

$keycolor = @{
Stopped = "Red"
Running = "Green"
StartPending = "Magenta"
ContinuePending = "Cyan"
StopPending = "Magenta"
PausePending = "Cyan"
Paused = "Yellow"
}

These are the same values as the status enumeration. I still intend to use a regular expression so I will create one from the hashtable keys.

$keys = $keycolor.keys -join "|"

The principal is the same as the previous solution but the hashtable makes it a bit more dynamic.

foreach ($line in $split) {
    If ($line -match $keys) {
        [string]$m = $matches.Values[0].trim()
        #get index of match
        $i = $line.IndexOf($m)
        $line.Substring(0,$i) | Write-Host -NoNewline
        $line.Substring($i) | Write-Host -ForegroundColor $keyColor.item($m)
    }
    else {
        #just write line
        Write-Host $line
    }
}

The end result is the same.

Different technique same results (Image Credit: Jeff Hicks)
Different technique same results (Image Credit: Jeff Hicks)

I hope you’ve seen that there are many ways you use PowerShell to meet your needs, but realize that these techniques are the exceptions. All I’ve done is show you how to make a pretty screen that you can read. That’s it.

With that said, these techniques may fit a need, and I think we’ve reached the point where we need to expand on them and make them flexible enough to handle any type of output. More to come, so keep an eye out for new content.