Thursday, October 29, 2009

Using F# BigInt in PowerShell

Occasionally I have the need to utilize large precise numbers within PowerShell. Unfortunately, the native data types that we can use are limited in their precision.
  • Float: 7 digits
  • Double: 16 digits
  • Decimal: 29 digits

I have been researching F# and came across a data type called BigInt. I now knew that I could go back to PowerShell and take advantage of this type.

The process to do so is straight forward:
First add a reference to the FSharp.Core assembly

Add-Type -Path 'C:\Program Files (x86)\FSharp-1.9.6.16\bin\fSharp.Core.dll'

Now you do things like:
PS C:> [Microsoft.FSharp.Math.BigInt]::Pow(17,71).toString()
PS C:>2300771122759378216336589429524308
0278517973481542540644504860767306
72752491528319986033

So if you are a fan of http://projecteuler.net/ you now have another tool you can utilize.

Enjoy!

Friday, October 2, 2009

Defining Object Formatting or "How to always show Military time"

A former colleague of mine at the OSU Medical Center recently asked a PowerShell related question.

Wait - Stop - Let's start over. I must digress a moment, please be patient as I give this former colleague of mine an appropriate introduction.

This Buckeye transplant to the state of Texas is much more then a former workmate, his legacy persists in innumerable ways. I won't take your time listing many of them, but I do have to share at least one. Notice the name of my blog? It isn't named after a South Park character, nor is it named after some pirate fetish. No this transplanted Buckeye is the source of FatBeard. He has a peculiar ability to derive nicknames that persist way beyond any code or script he has ever written. I fell prey to his moniker making over 3 years ago.

OK - now that you know how FatBeard came to be, lets move on with the PowerShell related post.

Texas asked me the following:
When I type Get-ChildItem (dir, ls) and get a file listing I want to see the LastWriteTime in Military format. Easy enough! I sent him this:

Get-ChildItem | select @{Name="LWT";Expression={"{0:d} {0:HH}:{0:mm}" -f ($_.LastWriteTime)}}

His response went something like this (paraphrased for the children in the audience) . "You obtuse gluteus maximus, why would I want to type that each and every time I need a directory listing." Admittedly, he had a point.

Thus begins my first foray into extending types. Using this as my guide (http://msdn.microsoft.com/en-us/library/ms714665(VS.85).aspx), I was able to quickly accomplish the task.

My steps were as follows:

  1. Navigate to C:\Windows\System32\WindowsPowerShell\v1.0 and make a copy of FileSystem.format.ps1xml
  2. Rename the copied file (CustomFileSystem.format.ps1xml)
  3. Open the renamed file and look for the tag.
  4. You should find something like this -
    [String]::Format("{0,10} {1,8}", $_.LastWriteTime.ToString("d"), $_.LastWriteTime.ToString("t"))
  5. As you can see, this handles the LastWriteTime formating for FileSystem related information. All we need to do is change it to meet our need. This should do -
  6. [String]::Format("{0,10} {1,2}:{2,2}:{3,2}", $_.LastWriteTime.ToString("d"),
    $_.LastWriteTime.ToString("HH"), $_.LastWriteTime.ToString("mm"),
    $_.LastWriteTime.ToString("ss"))
  7. Save the file.
  8. We now need to load the new formatting. Easy enough!
    Update-FormatData -prependPath 'C:\WINDOWS\system32\windowspowershell\v1.0\ CustomFileSystem.format.ps1xml'
  9. Verify your custom formatting by typing a few Get-ChildItem commands.
Now that wasn't to hard was it! Well Texas is happy now and you know how to modify object formatting (and how FatBeard came to be).

Enjoy!

Tuesday, August 25, 2009

PowerShell and ZedGraph – Example 3

Welcome to part three in our PowerShell/ZedGraph series. Make sure you take a look at Part 1 & Part 2 before you continue on. Before we jump into more advanced features, I thought I would demonstrate how to populate the chart with real data. Apparently some were not impressed by the Yuengling consumption example (a fictitious one at that!).

We will use the ever diligent Get-Process cmdlet for our "real" data. The end result will look something like this:


Pretty no?

The code:
#Load the zedgraph dll
$ZedGraphDll = "C:\zedgraph_dll_v5.1.5\ZedGraph.dll"
[System.Reflection.Assembly]::LoadFrom($ZedGraphDll) | out-null

# Create a WinForm to serve as a container
$global:form = new-object Windows.Forms.form
$form.Size = new-object System.Drawing.Size @(500,400)

# Create a ZedGraphControl
$zgc = new-object -typename ZedGraph.ZedGraphControl
$zgc.GraphPane.Title.Text = "Processes"
$zgc.GraphPane.XAxis.Title.Text = "Process"
$zgc.GraphPane.YAxis.Title.Text = "WS(MB)"

# Go fetch me some processes data
$WS = @{Name='WS';Expression={"{0:#}" -f ($_.WorkingSet/1KB)}}
$processes = Get-Process | where {$_.WorkingSet -gt 30MB} | Sort Name | select $WS, Name

$a = $b = @()
foreach($Process in $Processes)
{
$a+= $Process.WS
$b+= $Process.Name
}

$curve = $zgc.GraphPane.AddBar("Test",$null,$a,[System.Drawing.Color]::Red)
$zgc.GraphPane.XAxis.Type = 'Text'
$zgc.GraphPane.XAxis.Scale.FontSpec.Angle = 65
$zgc.GraphPane.XAxis.Scale.MajorStep = 1
$zgc.GraphPane.X2Axis.Scale.FontSpec.Size = 8
$zgc.GraphPane.XAxis.Scale.TextLabels = $b

# Hide the legend
$zgc.GraphPane.Legend.IsVisible = $False

# Make me pretty...
$zgc.GraphPane.Fill = New-Object ZedGraph.Fill([System.Drawing.Color]::WhiteSmoke,[System.Drawing.Color]::Lavender,0)
$zgc.GraphPane.Chart.Fill = New-Object ZedGraph.Fill([System.Drawing.Color]::FromArgb(255,255,245), [System.Drawing.Color]::FromArgb(255,255,190),90)

# Calculate the Axis Scale Ranges
$zgc.AxisChange()

# Add our graph to the form
$Form.Controls.Add($zgc)
$zgc.dock = [System.Windows.Forms.DockStyle]::Fill

# Show the form
$Form.Add_Shown({$form.Activate()})
[void]$form.showdialog()
The only difference in this example is we create and populate a couple arrays (forgive my naming conventions) and use them as our data points.

I hope I have answered some of the questions about "real" data utilization with ZedGraph.

Enjoy!

Monday, August 24, 2009

PowerShell and ZedGraph – Example 2

If you haven't already, take a look at Example 1 before you move forward!
Let's continuing with our charting example. At this point we have created a simple bar chart. Let's make a couple of modifications.
The first thing we will do is to remove the legend.
All we need to do is set the Legend.IsVisible property:
$zgc.GraphPane.Legend.IsVisible = $False
Next, let's apply some color. We will set the pane background and the Axis background with a gradient:
$zgc.GraphPane.Fill = New-Object ZedGraph.Fill([System.Drawing.Color]::WhiteSmoke,[System.Drawing.Color]::Lavender,0)

$zgc.GraphPane.Chart.Fill = New-Object ZedGraph.Fill([System.Drawing.Color]::FromArgb(255,255,245), [System.Drawing.Color]::FromArgb(255,255,190),90)

Lastly, lets add some text to the graph:
$text = New-Object ZedGraph.TextObj("Supply`nDepleted",5,30)
$zgc.GraphPane.GraphObjList.Add($text)
$arrow = New-Object ZedGraph.ArrowObj([System.Drawing.Color]::Black,10,5,26,5,15)
$zgc.GraphPane.GraphObjList.Add($arrow)

So our end result should look something like this:

The code...
#Load the zedgraph dll
$ZedGraphDll = "C:\zedgraph_dll_v5.1.5\ZedGraph.dll"
[System.Reflection.Assembly]::LoadFrom($ZedGraphDll) | out-null

# Create a WinForm to serve as a container
$global:form = new-object Windows.Forms.form
$form.Size = new-object System.Drawing.Size @(500,400)

# Create a ZedGraphControl
$zgc = new-object -typename ZedGraph.ZedGraphControl
$zgc.GraphPane.Title.Text = "Yuengling Consumption"
$zgc.GraphPane.XAxis.Title.Text = "Month"
$zgc.GraphPane.YAxis.Title.Text = "Bottles"

$xLabels = "April", "May", "June","July", "August"
$yLabels = 60, 60, 75, 70, 15

$zgc.GraphPane.AddBar("Ying",$null,$yLabels,[System.Drawing.Color]::Red)
$zgc.GraphPane.XAxis.Type = 'Text'
$zgc.GraphPane.XAxis.Scale.TextLabels = $xLabels

# Hide the legend
$zgc.GraphPane.Legend.IsVisible = $False

# Fill the pane background with a gradient
$zgc.GraphPane.Fill = New-Object ZedGraph.Fill([System.Drawing.Color]::WhiteSmoke,[System.Drawing.Color]::Lavender,0)

# Fill the Axis Background with a gradient
$zgc.GraphPane.Chart.Fill = New-Object ZedGraph.Fill([System.Drawing.Color]::FromArgb(255,255,245), [System.Drawing.Color]::FromArgb(255,255,190),90)

# Add text item to decorate the graph
$text = New-Object ZedGraph.TextObj("Supply`nDepleted",5,30)
$zgc.GraphPane.GraphObjList.Add($text)

# Add an arrow pointer for the above text line
$arrow = New-Object ZedGraph.ArrowObj([System.Drawing.Color]::Black,10,5,26,5,15)
$zgc.GraphPane.GraphObjList.Add($arrow)

# Calculate the Axis Scale Ranges
$zgc.AxisChange()

# Add our graph to the form
$Form.Controls.Add($zgc)
$zgc.dock = [System.Windows.Forms.DockStyle]::Fill

# Show the form
$Form.Add_Shown({$form.Activate()})
[void]$form.showdialog()
Till next time - Enjoy!

Sunday, August 23, 2009

PowerShell and ZedGraph – Example 1

Was searching for a PowerShell Mathematics library when I stumbled upon a cool .NET drawing library that can be easily consumed in PowerShell –ZedGraph. This is a fairly robust library that will enable you to graph multiple types of charts, including overlays. I plan on writing a series of posts that will demonstrate these capabilities.

Lets get started!

For the first chart, we are going to keep it simple. We will create a standard Bar chart with some fictitious data. The end result will look something like this.


Now to the code:

# Load the ZedGraph dll   
$ZedGraphDll = "C:\zedgraph_dll_v515\zedgraph_dll_v5.1.5\ZedGraph.dll"
[System.Reflection.Assembly]::LoadFrom($ZedGraphDll) | out-null

# Create a WinForm to serve as a container
$global:form = new-object Windows.Forms.form
$form.Size = new-object System.Drawing.Size @(500,400)

# Create a ZedGraphControl
$zgc = new-object -typename ZedGraph.ZedGraphControl
$zgc.GraphPane.Title.Text = "Yuengling Consumption"
$zgc.GraphPane.XAxis.Title.Text = "Month"
$zgc.GraphPane.YAxis.Title.Text = "Bottles"

$xLabels = "April", "May", "June","July", "August"
$yLabels = 60, 60, 75, 70, 15

$zgc.GraphPane.AddBar("Ying",$null,$yLabels,[System.Drawing.Color]::Red)
$zgc.GraphPane.XAxis.Type = 'Text'
$zgc.GraphPane.XAxis.Scale.TextLabels = $xLabels

# Calculate the Axis Scale Ranges
$zgc.AxisChange()

# Add our graph to the form
$Form.Controls.Add($zgc)
$zgc.dock = [System.Windows.Forms.DockStyle]::Fill

# Show the form
$Form.Add_Shown({$form.Activate()})
[void]$form.showdialog()

As you can see, it is fairly simple to get a standard graph output. The class documentation on the ZedGraph site was very helpfull. Make sure you spend some time looking over it as you start to explore this dll. I will continue to build on this example over the next few days.

Enjoy!

Wednesday, August 12, 2009

2009 Summer Scripting Games – Beginner Event 3

What a pleasant surprise! Came into work this morning and saw that @makovec sent me a note indicating that this mornings Scripting Guys Blog referenced one of my submissions posted during the 2009 Summer Scripting Games!

The posting sited the use of the “undocumented” parameter –delimiter.

Their post is here.

It isn’t very often that Stahler and elegant are used in the same sentence…

Wednesday, August 5, 2009

Removing Header/Footer lines from CSV files

I was recently tasked with scheduling a script to read in a TSM.out file and create a usable CSV file from it. The TSM *.out file has at least 9 header lines that are not relevant to the end file as well as some extra footer lines. I also noticed that the column headers are not descriptive either. So, to automate the file transformation, we look to PowerShell!
function Create-CSV {
param( [int]$HeaderLines,
[int]$FooterLines,
[string]$SourceFilePath,
[string]$DestinationFilePath,
[string]$NewColumnNames )

if(Test-Path -literalPath $SourceFilePath) {
$a = Get-Content -path $SourceFilePath
if ($NewColumnNames -ne $null) {
Add-Content -path $DestinationFilePath `
-value $NewColumnNames
}
# Grab only the lines we need
$a[$HeaderLines..($a.count - $FooterLines)] | `
foreach{Add-Content -path $DestinationFilePath -value $_}
}
else {
Write-Error "$SourceFilePath does not exist."
}
} # End of function

Create-CSV -HeaderLines 9 `
-FooterLines 4 `
-SourceFilePath 'c:\temp\test.out' `
-DestinationFilePath 'c:\temp\test.csv' `
-NewColumnNames "Nodename,hostname,tcpipaddress"

Enjoy!