Tag Archives: Write-Host

PowerShell Console Graphing Revised

Many of you have been having fun with my PowerShell Console Graphing tool I posted the other day. But I felt the need to make one more major tweak. I wanted to have the option for conditional formatting. That is, display graphed entries with high values in one color, medium in another and low in yet another.

The default behavior is still to use a single color. But using ParameterSets I added some new parameters, -HighColor, -MediumColor and -LowColor. They are all mandatory so if you use one parameter you have to define them all.


[cmdletbinding(DefaultParameterSetName="Single")]
Param (
[parameter(Position=0,Mandatory=$True,HelpMessage="Enter a property name to graph")]
[ValidateNotNullorEmpty()]
[string]$Property,
[parameter(Position=1,ValueFromPipeline=$True)]
[object]$Inputobject,
[string]$CaptionProperty="Name",
[string]$Title="$Property Report - $(Get-Date)",
[Parameter(ParameterSetName="Single")]
[ValidateNotNullorEmpty()]
[System.ConsoleColor]$DefaultColor="Green",
[Parameter(ParameterSetName="Conditional",Mandatory=$True)]
[ValidateNotNullorEmpty()]
[System.ConsoleColor]$HighColor,
[Parameter(ParameterSetName="Conditional",Mandatory=$True)]
[ValidateNotNullorEmpty()]
[System.ConsoleColor]$MediumColor,
[Parameter(ParameterSetName="Conditional",Mandatory=$True)]
[ValidateNotNullorEmpty()]
[System.ConsoleColor]$LowColor,
[alias("cls")]
[switch]$ClearScreen
)

I also moved the Property parameter and made it positional which should make it easier to use. The conditional coloring works basically by taking the largest possible graph value, which is based on available screen width and dividing it into thirds. The top third is considered high, second third is medium and last third is low.


...
#get remaining available window width, dividing by 100 to get a
#proportional width. Subtract 4 to add a little margin.
$available = ($width-$longest-4)/100
Write-Verbose "Available value is $available"

#calculate high, medium and low ranges based on available
$HighValue = ($available*100) * 0.6666
$MediumValue = ($available*100) * 0.3333
#low values will be 1 to $MediumValue
Write-Verbose "High value will be $HighValue"
Write-Verbose "Medium value will be $MediumValue"
...

When it comes time to graph, I check which parameter set we’re using and set the graph color accordingly.


...
if ($pscmdlet.ParameterSetName -eq "Single") {
$GraphColor = $DefaultColor
}
else {
#using conditional coloring based on value of $graph
if ($Graph -ge $HighValue) {
$GraphColor = $HighColor
}
elseif ($graph -ge $MediumValue) {
$GraphColor = $MediumColor
}
else {
$GraphColor = $LowColor
}
}
Write-Host ($g*$graph) -ForegroundColor $GraphColor
...

But now I can run a command like this:


PS Scripts:\> ps | where {$_.cpu} | out-consolegraph CPU -high Red -medium magenta -low yellow -cls

out-consolegraph-3

What do you think? Download Out-ConsoleGraph-v2.

Graphing with the PowerShell Console

I’ve written before about using the PowerShell console as a graphing tool, primarily using Write-Host. Most of what I’ve published before were really proof of concept. I decided to try and come up with a more formal and re-usable tool that could create a horizontal bar graph based on a numeric property from piped objects. For example, I wanted to get all processes and display a graph of the WorkingSet for each object. My result is an advanced function that should work in v2 or v3 called Out-ConsoleGraph.

Here’s the code, minus, the comment-based help. I’ve commented the quote quite a bit so I won’t spend a lot of time explaining it in detail.


Function Out-ConsoleGraph {

#comment based help goes here

[cmdletbinding()]
Param (
[parameter(Position=0,ValueFromPipeline=$True)]
[object]$Inputobject,
[parameter(Mandatory=$True,HelpMessage="Enter a property name to graph")]
[ValidateNotNullorEmpty()]
[string]$Property,
[string]$CaptionProperty="Name",
[string]$Title="$Property Report - $(Get-Date)",
[ValidateNotNullorEmpty()]
[System.ConsoleColor]$GraphColor="Green",
[alias("cls")]
[switch]$ClearScreen
)

Begin {
Write-Verbose -Message "Starting $($MyInvocation.Mycommand)"
#get the current window width so that our lines will be proportional
$Width = $Host.UI.RawUI.BufferSize.Width
Write-Verbose "Width = $Width"

#initialize an array to hold data
$data=@()
} #begin

Process {
#get the data
$data += $Inputobject

} #end process

End {
#get largest property value
Write-Verbose "Getting largest value for $property"
Try {
$largest = $data | sort $property | Select -ExpandProperty $property -last 1 -ErrorAction Stop
Write-Verbose $largest
}
Catch {
Write-Warning "Failed to find property $property"
Return
}
If ($largest) {
#get length of longest object property used for the caption so we can pad
#This must be a string so we can get the length
Write-Verbose "Getting longest value for $CaptionProperty"
$sample = $data |Sort @{Expression={($_.$CaptionProperty -as [string]).Length}} |
Select -last 1
Write-Verbose ($sample | out-string)
[int]$longest = ($sample.$CaptionProperty).ToString().length
Write-Verbose "Longest caption is $longest"

#get remaining available window width, dividing by 100 to get a
#proportional width. Subtract 4 to add a little margin.
$available = ($width-$longest-4)/100
Write-Verbose "Available value is $available"

if ($ClearScreen) {
Clear-Host
}
Write-Host "$Title`n"
foreach ($obj in $data) {
#define the caption
[string]$caption = $obj.$captionProperty

<#
calculate the current property as a percentage of the largest
property in the set. Then multiply by the remaining window width
#>
if ($obj.$property -eq 0) {
#if property is actually 0 then don't display anything for the graph
[int]$graph=0
}
else {
$graph = (($obj.$property)/$largest)*100*$available
}
if ($graph -ge 2) {
[string]$g=[char]9608
}
elseif ($graph -gt 0 -AND $graph -le 1) {
#if graph value is >0 and <1 then use a short graph character
[string]$g=[char]9612
#adjust the value so something will be displayed
$graph=1
}

Write-Verbose "Graph value is $graph"
Write-Verbose "Property value is $($obj.$property)"
Write-Host $caption.PadRight($longest) -NoNewline
#add some padding between the caption and the graph
Write-Host " " -NoNewline
Write-Host ($g*$graph) -ForegroundColor $GraphColor
} #foreach
#add a blank line
Write-Host `n
} #if $largest
Write-Verbose -Message "Ending $($MyInvocation.Mycommand)"
} #end

} #end Out-ConsoleGraph

The function requires that PowerShell be running in STA mode, which shouldn't really be an issue. The intent is that you will be piping objects to the function. You need to specify a property that you want to graph and an object property to use as the label or caption for each object. The default caption is the Name property which seems pretty common. The property you are graphing must have a numeric value. The function's premise is to get the window width, then write the caption and a graph figure using the remaining available width. The function has a bit of code to calculate the longest caption value so that everything lines up and then determines how much space remains for graphing.

The graph is really more of a proportional representation as opposed to actual value. In short, I find the largest property value which essentially becomes the 100% mark. All other values are calculated as percentages and graphed accordingly. This might be easier to understand if you see it in action.


PS Scripts:\> get-process | where {$_.company -notmatch "microsoft"} | out-consolegraph -property WorkingSet -cls

This is getting all non-Microsoft processes and creating a graph of the WorkingSet property.

out-consolegraph-1

The graph title and color are customizable via parameters. This should work for any type of object as long as you can have a numeric property.


Get-ChildItem C:\Scripts -Directory | foreach {
$data = Get-ChildItem $_.FullName -Recurse -File | Measure-Object -Property Length -sum
$_ | Select Name,@{Name="Size";Expression={$data.sum}}
} | Out-ConsoleGraph -Property Size -Title "Scripts Folder Report" -GraphColor Cyan

out-consolegraph-2

This command will work in the PowerShell ISE but I think it works better in the PowerShell console. Remember, this command is NOT writing to the pipeline so all you can do is view the output.

Download Out-ConsoleGraph and let me know what you think.

Valentines Day PowerShell Fun

In case you missed some of the fun on Twitter and Google Plus, here are the PowerShell valentines I sent today. These are intended to be run from the PowerShell console, not the ISE. Also, depending on your console font, you might get slightly different results. I use Lucida Console.

 

 


#1
write-host (([char]3 -as [string])*14) -ForegroundColor red

#2
$s="Be My Valentine"
[string]$hrt=[char]3

$s.split() | foreach -begin {$a="" }{$a+= "$_$hrt"} -end {write-host $a -ForegroundColor Magenta}

#alternative
$s.split() | foreach {Write-Host $_ -nonewline -foregroundcolor Magenta; Write-Host $hrt -nonewline -ForegroundColor Red} -end {"`n"}

#3
Write-Host "U make my $([char]3) $([char]14)" -foregroundcolor magenta -backgroundcolor yellow

#variation from Josh Atwell
Write-Host "Wild thing!" -ForegroundColor Yellow -BackgroundColor Magenta
Start-Sleep -Seconds 1
Write-Host "U make my $([char]3) $([char]14)" -ForegroundColor magenta -BackgroundColor yellow

#4
write-host "$([char]12)$([char]3)$([char]11)=$([char]2)" -foregroundcolor red -backgroundcolor white

Download MoreValentines.ps1 But I recommend you accompany this with at least a bouquet of nice flowers.

Friday Fun PowerShell Valentines Day

With Valentine’s Day just around the corner, I thought I should help you out with your gift giving. Remember those bags of candy with the cute sayings like “Be Mine”? Here’s how you can create a “bag” of them using Windows PowerShell; perfect for that extra geeky significant other. Or maybe you’ll just have fun playing with this.

#change the window title
$host.ui.RawUI.WindowTitle="Happy Valentine's Day"

#define an array of sentiments
$print=@("Be mine","Hey cutie","Be my valentine","I luv u","I$([char]3)U","Love",
"Romance","Kiss me","Hug me","My Girl","UR the 1","Love me tender","Luv2Luv",
"Hold Me","Sweetie","Honey","HunnyBunny","$([char]3)2$([char]3)","4Ever","XXOOXX")

#define the colors to use for the candy
$color="Cyan","Magenta","Yellow","White","Red","DarkRed","Green"

#enter a number greater than 100 for maximum impact
$count=Read-Host "How many sweethearts do you want?"

#create the candy elements
1..$count | foreach -begin {cls} -process {
Write-Host "$(get-random $print) " -foreground (get-random $color) -nonewline
} -end {write-host "`n"}

The first part of the script changes the console title. Nothing extra special there, in terms of PowerShell. Next, I’m defining an array of strings that will be “printed” on the “candy”. I’m also defining an array of colors that you can use with Write-Host. Some colors like Gray hardly seem romantic so I’m limiting my array to the “pretty” colors. Finally, the script prompts you for the number of candies to create.

The main part of the script is a Foreach-Object expression. I’m using the range operator (..) as a counter. Each number is piped to ForEach, but I’m not doing anything with it. What I am doing though is using other cmdlet parameters that you may not be used to seeing. Most of the time when we use ForEach-Object, the scriptblock is the -Process parameter value, which runs once for every object piped in. But in my script I’m also using the -Begin parameter. This scriptblockk executes once before any pipelined objects are processed. In my script I’m simply clearing the screen. In the process script block I’m using Write-Host to write a random string from the $Print array using a random foreground color from the $color array. I’m also using -NoNewLine because I want to fill the screen, (or bag in keeping with my analogy). If I didn’t, I’d get a list. But because I’m not writing a new line, when I’ve reached the maximum number of candies, the -End scriptblock runs which simply adds the necessary return.

Download Valentines.ps1

Happy Valentine’s Day.

Friday Fun: Output to 2 Places in 1

Today’s Friday Fun comes out of a short exchange I had yesterday with Hal Rottenberg on Google Plus. We were playing around with piping a PowerShell command to Clip.exe which dumps the output to the Windows Clipboard. I got to thinking about taking this a step further based on my needs as a writer. Often I’d like to see the results of a command and then copy and paste the results into whatever I’m working on. In other words, I need to TEE the output to two places.

PowerShell has a cmdlet called Tee-Object that follows this principal. The default behavior is to write output to the pipeline AND send it to a text file.


PS C:\> get-service | tee c:\work\svc.txt

I’ll see the results and save them to a text file. I can also use this cmdlet to save results to a variable.


PS C:\> get-service | tee c:\work\svc.txt

Status Name DisplayName
------ ---- -----------
Running AeLookupSvc Application Experience
Stopped ALG Application Layer Gateway Service
Stopped AppIDSvc Application Identity
Stopped Appinfo Application Information
...
Running wudfsvc Windows Driver Foundation - User-mo...
Stopped WwanSvc WWAN AutoConfig

PS C:\> $svc.count
196
PS C:\>

One approach I came up with to incorporate with Clip.exe was this:


PS C:\> get-service | tee -Variable svc | clip

I don’t get the results immediately to the screen; they are saved to the variable. But at the same time output has been directed to the Windows Clipboard. That could be useful. But you know me, I always have to tinker a bit more and I ended up with a function called Out-Tee.


Function Out-Tee {

[cmdletbinding()]

Param (
[Parameter(Position=0,Mandatory=$True,ValueFromPipeline=$True)]
[object[]]$InputObject,
[alias("foregroundcolor","fg")]
[string]$TextColor=$host.ui.rawui.ForegroundColor
)

Begin {
#define an empty array to hold piped in objects
$a=@()
}

Process {
#add each piped in object to the array
$a+=$inputobject
}

End {
#write the array to the pipeline as a string then pass to Write-Host
$a | out-string | write-host -fore $textColor
#write the array again to Clip.exe
$a | clip
}

} #end function

This simple function takes a PowerShell expression and writes the results to the console using Write-Host and also to the clipboard. The default output will use the current console foreground color. But you can specify any other color that you would use with Write-Host. I even added some alias properties so you can use -foregroundcolor or -fg.

With this function I can see the result and have it dumped to the clipboard. Because the default text color is the same as my session, I don’t see any difference when using Out-Tee.


PS C:\> ps | where {$_.ws -gt 100mb} | out-tee

Or if I want to pretty it up, I can add a little color.


PS C:\> ps | where {$_.ws -gt 100mb} | out-tee -TextColor green

Of course, the clipboard is just text. But now I have something easier to use to save output to the clipboard so I can paste it into my documents, assuming I like the output I see on the screen. The one caveat is that this function only works with successful commands. Errors, warnings, or verbose statements won’t get dumped to the clipboard. I can think of some ways around that which I might try in a future version. But for my immediate needs this works just fine.

Download Out-Tee and give it a try.

Friday Fun Drive Usage Console Graph

I think you’ll like this. Normally, I prefer my PowerShell commands to write objects to the pipeline. But there’s nothing wrong with sending output directly to the console, as long as you know that the output is intended only for the screen. What I find frustrating is the use of Write-Host when really, pipelined objects would be better. But for today, I’m going to revel in the beauty of the console and create a colorized drive utilization graph. Continue reading

Friday Fun: Start-TypedDemo v2

Not too long ago I posted a function I wrote for doing PowerShell demonstrations. My goal was to simulate a live interactive demo but without the typing so I could focus on explaining and not typing. The first version was a good start but I always had plans for a more feature complete function including typos, support for multiline expressions and transcription. That’s what I have for today’s Friday Fun. Continue reading