Saturday, September 1, 2007

Colorize matching output in the pipeline

 

The other day I was looking for an option to colorize certain rows when using Format-* cmdlets. None of the cmdlets (including out-host, out-default) gives you the option to do that unless you define a custom PowerShell format/view file (ps1xml). I wish the Format-* cmdlets had a scriptblock parameter to which I can pass a piece of code that will colorize certain rows based on a criteria (maybe I'll fill a suggestion on Microsoft connect).

The major drawback of this custom format files is that you have to build them (requires some experience) for each type you want to print out, loading them into your environment via Update-FormatData and then executing them with the -view  parameter of the format-table or format-custom cmdlets.

This method involves too much configuration and I was looking for a way to make the process more portable, depend less and operatable on any given command-let,  so I can easily use it on any PowerShell machine or script.


Lets say you want to colorize all text file names that begins with "s".

 

PS C:\Scripts> Get-ChildItem $env:WINDIR *.txt

    Directory: Microsoft.PowerShell.Core\FileSystem::C:\WINDOWS

Mode       LastWriteTime               Length       Name
----          -------------                     ------         ----
-a---         7/28/2007 3:25 PM       8184          ModemLog_Agere Systems HDA Modem.txt
-a---         6/21/2007 11:43 AM     38280        ModemLog_HTC USB Modem.txt
-a---         3/5/2007   6:17 PM       3075         OEWABLog.txt
-a---         8/30/2007 10:51 AM     32622        SchedLgU.Txt
-a---         3/1/2007   1:56 AM       1012440    setuplog.txt
-a---         7/31/2006  7:41 PM       726072     SIGVERIF.TXT

 

Get-ChildItem $env:WINDIR *.txt | foreach {
    if($_.name -match "^s.*"){
        write-host $_ -ForegroundColor "green";
    } else {
        write-host $_ -ForegroundColor "gray"
    }
}

 

ModemLog_Agere Systems HDA Modem.txt
ModemLog_HTC USB Modem.txt
OEWABLog.txt
SchedLgU.Txt
setuplog.txt
SIGVERIF.TXT


As you can see, the foreach output contains only file names, no table headers nor additional property columns. It sure doesn't look like Format-Table's output.


For about an hour or so I was struggling with PowerShell commands and made a lot of experiments. Finally, I was able to construct a filter. The filter accepts three parameters.
Color, property name to match against  and a regular expression pattern.
Surprisingly, the solution was pretty simple. Without the filter the code looks like:


Get-ChildItem $env:WINDIR *.txt | foreach {
    if($_.name -match "^s.*"){
        [console]::ForegroundColor="green"; $_;
    } else {
        [console]::ForegroundColor="white"; $_;
    }
}

 

Well, here's the code for the filter:

 

filter colorize-row{
    param(
        [string]$color="green",
        [string]$prop="name",
        [string]$regex=$(throw "must supply regular expression pattern")

    )
    
    # save current console colors
    #$bgc=[console]::BackgroundColor;
    $fgc=[console]::ForegroundColor;

    if($_.$prop -match $regex){[console]::ForegroundColor=$color; $_}
    else{[console]::ForegroundColor=$fgc; $_}

    # revert to saved console colors
    #[console]::BackgroundColor=$bgc;
    [console]::ForegroundColor=$fgc;
}

 

# get all text files in c:\windows that begins with "s", use default filter color 

PS C:\Scripts> Get-ChildItem $env:WINDIR *.txt | colorize-row -regex "^s.*"

    Directory: Microsoft.PowerShell.Core\FileSystem::C:\WINDOWS

Mode       LastWriteTime               Length       Name
----          -------------                     ------         ----
-a---         7/28/2007 3:25 PM       8184          ModemLog_Agere Systems HDA Modem.txt
-a---         6/21/2007 11:43 AM     38280        ModemLog_HTC USB Modem.txt
-a---         3/5/2007   6:17 PM       3075         OEWABLog.txt
-a---         8/30/2007 10:51 AM     32622        SchedLgU.Txt
-a---         3/1/2007   1:56 AM       1012440    setuplog.txt
-a---         7/31/2006  7:41 PM       726072     SIGVERIF.TXT

 

Now with get-process:

# get all processes which contains "svchost" in their names, colorize in yellow

PS C:\Scripts> get-process | colorize-row -regex "svchost" -prop "name" -color yellow

Handles  NPM(K)    PM(K)    WS(K) VM(M)   CPU(s)   Id      ProcessName
-------      ------     -----       -----    -----      ------     --      -----------
...
...
     68       3           816       1264    14        0.02      524     sqlbrowser
    311       9          34172    20632  1100    1.02      436     sqlservr
     84       2           1064     1616     20       0.44      548     sqlwriter
   1894     246        34804   38528   217     197.80   180     svchost
    104       5          1772     2336     33       10.56    332     svchost
    126       4          2616     2160     36       3.80      580     svchost
    284      12         8124     5084     47       2.45      1152    svchost
     75       3          2332     1468     31       0.06       1512   svchost
    230       6         3464     2776     65       72.11     1640   svchost
    428      14        2368     2852     40       15.72     1692   svchost
     95      10        1796     1460      37       0.28       2696   svchost

    807       0        0          244         2        359.94    4        System
    207       7        6608     9788      62       114.42   5828    TextPad

...

...


 

About PowerShell Console Colors

You can specify one of the following enumeration values to the filter -color parameter. To get all possible enumeration color names/values:

>> [consolecolor]::GetNames([System.ConsoleColor])
-or-
>> [consolecolor]::GetValues([System.ConsoleColor])


-or- better use the following to generate a table of color names and there corresponding numeric values:

 

$colors=@([enum]::GetNames([consolecolor]));
0..($colors.length-1) | select @{n="Name";e={[string][enum]::parse([ConsoleColor],$_)}}, @{n="Constant";e={$_}} | format-table -autosize

 

Name             Constant
----                   --------
Black                        0
DarkBlue                   1
DarkGreen                2
DarkCyan                  3
DarkRed                   4
DarkMagenta            5
DarkYellow                6
Gray                         7
DarkGray                  8
Blue                          9
Green                      10
Cyan                        11
Red                         12
Magenta                  13
Yellow                     14
White                     15

1 comment:

Per Østergaard said...

Hi $hay

Clever way of changing the color without affecting the normal output. Good stuff!