For today's fun with PowerShell, I thought I'd share my solutions for a recent Iron Scripter challenge. If you aren't familiar with these challenges, and you should be, they are designed to test your PowerShell skills and hopefully help you learn something new. There are challenges for all skill levels and you can tackle them at your leisure. The most recent challenge was billed as a "back-to-school" event. We were tasked with building a set of functions to solve the type of math problems you most likely faced as a child.
ManageEngine ADManager Plus - Download Free Trial
Exclusive offer on ADManager Plus for US and UK regions. Claim now!
For PowerShell beginners, you could have created mathematical expressions using the standard math operators. For example, calculate the area of a circle.
$diameter = 7
$r = $diameter/2
3.14*($r*$r)
That is pretty straightforward. More experienced PowerShell scripters probably used the [Math] .NET class. There are no native cmdlets for using this class. All of its methods and properties are static. Here's the [Math] equivalent of the above.
[math]::pi*[math]::pow($r,2)
How did I know what to use? Let PowerShell show you. In the console, type [math]:: and then hit Ctrl+Space. Assuming you have the standard PSReadline module loaded, you should see all of the static properties and methods.
If you run the expression above you will get 38.484510006475. You might want to format that number. Yes, you could use the -f operator, but then you end up with a string. I like keeping numbers as numbers if I can. If you simply wanted a rounded integer you could do this:
[math]::pi*[math]::pow($r,2) -as [int]
Or, if you want to trim decimal points, use the [Math] class and the Round() method.
$raw = [math]::pi*[math]::pow($r,2)
[math]::round($raw,2)
I split this into two steps for the sake of clarity. You could easily combine this into a single expression. Now my result is formatted to 2 decimal points and is still a number. Technically, it is a [double]. I used these techniques in my solutions, like this:
Function Get-CircleArea {
[cmdletbinding()]
Param(
[parameter(Mandatory, HelpMessage = "Enter a circle diameter between 1 and 10")]
[ValidateRange(1, 10)]
[double]$Diameter,
[ValidateRange(2, 8)]
[int]$Decimal = 2
)
Write-Verbose $($myinvocation.mycommand)
Write-Verbose "Calculating area of a circle with a diameter of $Diameter"
$pi = [math]::pi
$r = $Diameter / 2
Write-Verbose "Using a radius of $r"
$area = $pi * ([math]::pow($r, 2))
Write-Verbose "Raw area = $area"
[pscustomobject]@{
PSTypename = "PSCircleArea"
Diameter = $Diameter
Radius = $r
Area = [math]::round($area, $decimal)
}
} #Get-CircleArea
My function writes a custom object to the pipeline.
There's a reason I go to the effort of creating a custom object with a unique type name. Here is my solution for getting the volume of a cylinder.
More as a teaching opportunity than anything, I wanted to create a formatted view of this type of object. Custom formatting requires creating special ps1xml files which can be tedious. Instead, I used the New-PSFormatXML command from the PSScriptTools module.
$new = @{
FormatType = "Table"
GroupBy = "Form"
Properties = "Diameter","Radius","Formatted"
ViewName = "default"
Path = ".\pscylinder.format.ps1xml"
}
Get-CylinderVolume -Diameter 1 -Height 1 |
New-PSFormatXML @new
Update-FormatData .\pscylinder.format.ps1xml
This creates the ps1xml file and then loads it into my session. Now I get the formatted result I want.
Because I packaged the functions into a module, I also included the formatting file and export it in the module manifest.
If you are wondering where you can see all of this, the module is online at https://github.com/jdhitsolutions/PSBackToSchool. As you look through the functions pay attention to the little things I included:
- parameter validation
- defining aliases
- the use of verbose messages
- custom object creation
The whole point of the challenge is to learn and ideally have a little fun. I know I enjoyed this challenge more than I thought I would. Enjoy.