Friday Fun: Size Me Up

Part of day job involves creating training material, often in the form of video training for Pluralsight or articles for Petri.com. Since I usually am covering PowerShell I often need to capture a PowerShell session. And sometimes I want the screen to be a particular size. So over time I’ve created a few PowerShell tools to resize console and application windows. The PowerShell console window stores its dimension under $host.ui.rawui.windowsize.

These are the same settings you would see here:

As long as you use a value less than the buffer dimensions, you can modify the console window from a prompt. But it takes a step you might not realize. You can’t do this:

Instead, you can create a new type of object with your intended dimensions.

Then you can use this object as a value for the WindowSize property.

Naturally, I created a function to do this for me.

My function also includes code to support –WhatIf.

Of course now that I’ve shown you that I have an alternative. You can use the .NET class [System.Console] which has properties for width and height. And you can set these values independently.

You can’t discover this unless you know something of the .NET Framework, but you could have discovered $host which is why I showed you that first. Since I often need to record video at 1280×720 dimensions, I wrote a quick and dirty script to set my PowerShell console window to those dimensions.

Everything I’ve shown you so far is for the PowerShell console. But what about the ISE? You can’t use the techniques I’ve covered. Application windows are bit more complicated and I’m not going to go into the details. But I came across some code on GitHub (https://gist.github.com/coldnebo/1148334). I don’t do Minecraft but it didn’t take much to turn it into a re-usable function.

The code supports –WhatIf and defaults to the current application, which is presumably the PowerShell ISE.

But this is what actually gets set.

So if you wanted to include the title bar you would need to adjust accordingly.

All of this may not really be applicable to your work, but if you find a good use I hope you’ll let me know. Have a great weekend.

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.

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.

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

But now I can run a command like this:

out-consolegraph-3

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

UPDATE: A newer version is available at http://jdhitsolutions.com/blog/2013/12/updated-console-graphing-in-powershell/

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.

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.

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.

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.

Friday Fun: Another PowerShell Console Graph

Late last year I posted a demo script to create a horizontal bar graph in the PowerShell console. I liked it and many of you did as well. But I also wanted to be able to create a vertical bar graph, ie one with columns. This is much trickier since you have to tell PowerShell exactly where to “paint” the graph.

I’ve posted other articles on using the coordinates in the host and that is what I ended up doing for today’s Friday Fun. This demo script only works in the PowerShell console. It will run in the ISE but you won’t get the desired result. Let me post the code and then I’ll go through a few things.


Param([string]$computername=$env:computername)

Clear-Host

#get the data
$drives=Get-WmiObject -Class Win32_LogicalDisk -Filter "drivetype=3" -computername $computername

#define a set of colors for the graphs
$colors=@("Yellow","Magenta","Green","Cyan","Red")

#set cursor position
$Coordinate = New-Object System.Management.Automation.Host.Coordinates
$Coordinate.X= 10
$Coordinate.Y= [int]($host.ui.rawui.WindowSize.Height -5)

#save starting coordinates
$startY=$Coordinate.Y
$startX=$Coordinate.X

#counter for colors
$c=0

#adjust Y so we can write the caption
$Coordinate.Y+=1

foreach ($drive in $drives) {
#set the color to the first color in the array of colors
$color=$colors[$c]
$legend=$drive.DeviceID
#calculate used space value
$used=$Drive.Size - $Drive.FreeSpace
[int]$usedValue=($used/($drive.size))*10
#adjust for values less than 0 so something gets graphed
if ($usedValue -le 0) {
[int]$usedValue=($used/($drive.size))*50
}

#format usage as a percentage
$usedPer="{0:p2}" -f ($used/($drive.size))
#set the cursor to the new coordinates
$host.ui.rawui.CursorPosition=$Coordinate
#write the caption
write-host $legend -nonew
#move the Y coordinate up to start the graph
$coordinate.Y-=1

for ($i=$usedValue;$i -gt 0;$i--) {
$host.ui.rawui.CursorPosition=$Coordinate
#draw the color space for the graph
write-host " " -BackgroundColor $color -nonewline
#move Y up 1
$coordinate.y--
#repeat until we reach the $usedValue
}
#set new coordinate
$host.ui.rawui.CursorPosition=$Coordinate
#write the usage percentage at the top of the bar
write-host $usedPer -nonewline

#reset Y to where we started + 1
$Coordinate.Y=($startY+1)
#move X to the right
$coordinate.x+=8
#reset coordinates
$host.ui.rawui.CursorPosition=$Coordinate
#increment the color counter
$c++

#repeat for the next drive

} #foreach

#reset coordinates so we can write a legend
$coordinate.Y=$StartY+2
$coordinate.X=$startX
$host.ui.rawui.CursorPosition=$Coordinate
write-host ("Drive Usage for {0}" -f $drives[0].__SERVER)

#move cursor to bottom of the screen and write a blank line
$Coordinate.X=1
$coordinate.Y=[int]($host.ui.rawui.WindowSize.Height-2)
$host.ui.rawui.CursorPosition=$Coordinate
write-host ""

#your PowerShell prompt will now be displayed

This script gets drive usage and creates a vertical bar chart displaying disk utilization. Everyone loves a good drive space report and they always make good demos.

The script creates a System.Management.Automation.Host.Coordinates object, setting the value of X and Y that should be starting in the bottom left corner of the console.


#set cursor position
$Coordinate = New-Object System.Management.Automation.Host.Coordinates
$Coordinate.X= 10
$Coordinate.Y= [int]($host.ui.rawui.WindowSize.Height -5)
#save starting coordinates
$startY=$Coordinate.Y
$startX=$Coordinate.X

I’m also saving these values so I can reset. The script will have to “move” the cursor around the screen to draw the graphs. Once I calculate the values for each drive, I write a blank line using Write-Host with -Backgroundcolor to get the desired graphing effect.


for ($i=$usedValue;$i -gt 0;$i--) {
$host.ui.rawui.CursorPosition=$Coordinate
#draw the color space for the graph
write-host " " -BackgroundColor $color -nonewline
#move Y up 1
$coordinate.y--
#repeat until we reach the $usedValue
}

Notice after each write I move the Y point “up”, until I reach the limit of the current value. I set values as a percentage scaled to 10 so the graph doesn’t end up outside of the buffer. I also made an adjustment for low values that wouldn’t normally trigger a graph so that I get something written to the screen.


#calculate used space value
$used=$Drive.Size - $Drive.FreeSpace
[int]$usedValue=($used/($drive.size))*10
#adjust for values less than 0 so something gets graphed
if ($usedValue -le 0) {
[int]$usedValue=($used/($drive.size))*50
}

After drawing the graph I move the cursor position back to the beginning and write a legend.


#reset coordinates so we can write a legend
$coordinate.Y=$StartY+2
$coordinate.X=$startX
$host.ui.rawui.CursorPosition=$Coordinate
write-host ("Drive Usage for {0}" -f $drives[0].__SERVER)

Here’s the end result.

This script is really just a proof of concept. I haven’t created any functions to simply any of this or make it easy to use with other values. This is also a bit advanced so if you look at this and it makes your head hurt, don’t worry about it. You would only use something like this in special cases. Still, I’d like to know what you think, how it works for you and if you extend it to a more re-usable form.

Download demo-bargraph. The script will default to the local computer, but you can run specify any computer you want.


PS C:\Scripts\> .\demo-bargraph.ps1 Mycomputer

Enjoy and have fun.

Maximizing the PowerShell Console Title Bar

A few days ago Boe Prox posted some very nifty PowerShell modules for using the title bar as a ticker for RSS feeds like the weather. I thought this was an awesome idea and an easy way to take advantage of what would otherwise be unused screen space. I was especially intrigued with his use of timer objects and event subscriptions to manage the updating.

Naturally I decided to run with this. My main goal was to take Boe’s fundamental idea and turn it into something more re-usable or extensible. My result is a module called ConsoleTitle.


PS C:\> get-command -Module ConsoleTitle | Select Name

Name
----
Get-Inspiration
Get-SystemStat
Get-Timer
New-Timer
Remove-Timer
Set-ConsoleTitle
Set-TimerInterval
Start-TitleTimer

The overall premise is pretty simple, define a global variable $PSConsoleTitle and use a timer to periodically update the console title bar with this value. During the refresh interval you can run whatever code you like, however you like, to provide a new value to the variable. In the module I’ve included two sample commands, Get-SystemStat and Get-Inspiration. The former uses WMI to gather system information from the local computer.

The other command defines an array of slogans, sayings and suggestions and randomly selects one to use as the title bar text.

The module includes a few commands for working with timer objects. You can use New-Timer in your own scripts. Here’s the function.


Function New-Timer {

<# .Synopsis Create an event timer object .Description Create an event timer object, primarily to be used by the ConsoleTitle module. Each timer job will automatically be added to the global variable, $ConsoleTitleEvents unless you use the -NoAdd parameter. This variable is used by Remove-Timer to clear console title related timers. This function is called from within other module functions but you can use it to create non-module timers. .Parameter Identifier A source identifier for your timer .Parameter Refresh The timer interval in Seconds. The default is 300 (5 minutes). Minimum value is 5 seconds. .Parameter Action The scriptblock to execute when the timer runs down. .Parameter NoAdd Don't add the timer object to the $ConsoleTitleEvents global variable. #>

Param(
[Parameter(Position=0,Mandatory=$True,HelpMessage="Enter a source identifier for your timer")]
[ValidateNotNullorEmpty()]
[string]$Identifier,
[Parameter(Position=1)]
[validatescript({$_ -ge 5})]
[int]$Refresh=300,
[Parameter(Position=2,Mandatory=$True,HelpMessage="Enter an action scriptblock")]
[scriptblock]$Action,
[switch]$NoAdd
)

Write-Verbose ("Creating a timer called {0} to refresh every {1} seconds." -f $Identifier,$Refresh)

#create a timer object
$timer = new-object timers.timer
#timer interval is in milliseconds
$timer.Interval = $Refresh*1000
$timer.Enabled=$True

#create the event subscription and add to the global variable
$evt=Register-ObjectEvent -InputObject $timer -EventName elapsed –SourceIdentifier $Identifier -Action $Action

if (-Not $NoAdd) {
#add the event to a global variable to track all events
$global:ConsoleTitleEvents+=$evt
}
#start the timer
$timer.Start()

} #Function

And here’s how you might use it.


Function Get-Inspiration {

Param(
[Parameter(Position=0)]
[ValidateScript({$_ -ge 5})]
[int]$Refresh=600
)

#Define an array of pithy sayings, slogans and quotes

#we'll create as a globally scoped variable so you can add to it anytime you want from PowerShell
$global:slogans=@(
"PowerShell Rocks!",
"Energize!!",
"To Shell and Back",
"I am the Shell",
"PowerShell to the People",
"Powered by PS",
"PowerShell Rulez!",
"PowerShell Fanboy",
"I am the walrus",
"Those who forget to script are doomed to repeat their work.",
"Have you backed up files lately?",
"Is your resume up to date?",
"Is it Beer O'Clock yet?",
"With great power comes great responsibility",
"I came, I saw, I scripted.",
"$env:username, Open the pod bay doors."
)

$sb={ $global:PSConsoleTitle=$global:slogans | get-random }
#invoke the scriptblock
Invoke-Command $sb

New-Timer -identifier "SloganUpdate" -action $sb -refresh $refresh

#start the update timer if not already running
if (-Not (Get-EventSubscriber -SourceIdentifier "TitleTimer" -ea "SilentlyContinue")) {
Start-TitleTimer -refresh $refresh
}

} #function

Think of the module as a framework or SDK for building your own solutions. The module also includes an about topic. I hope you’ll download ConsoleTitleand let me know what you think.