PowerShell is a fairly easy language to learn, but to some extent, creating classes is a bit of a hidden concept. The way to do it is to wrap functions in a module and use Export-ModuleMember to make them externally visible. Here is an easy to understand example of implementing the Write-Progress cmdlet in an even easier way.
function New-Progressbar
{
[CmdletBinding()]
Param
(
# Total count
[Parameter(Mandatory=$true,
ValueFromPipelineByPropertyName=$false,
Position=0)]
[int]$TotalCount,
# Activity name
[Parameter(Mandatory=$false,
ValueFromPipelineByPropertyName=$false,
Position=1)]
[string]$ActivityName = "Running",
# Time estimation
[Parameter(Mandatory=$false,
ValueFromPipelineByPropertyName=$false,
Position=2)]
[boolean]$TimeEstimationEnabled = $true
)
# Create new module instance
$m = New-Module -ScriptBlock {
# Internal variables
$script:total = 1;
$script:current = 0;
$script:ActivityName = " ";
$script:startTime = Get-Date;
$script:timeEstimation = $false;
# Functions with obvious method names
function setActivityName($name) {$script:ActivityName = $name}
function setTotal($tot) { $script:total = $tot}
function getTotal($tot) { return $script:total}
function enableTimeEstimation() {$script:timeEstimation = $true}
function disableTimeEstimation() {$script:timeEstimation = $false}
# Progress the progressbar one step. Optional parameter Text for defining the status message
function Progress {
Param
(
[Parameter(Mandatory=$false,
ValueFromPipelineByPropertyName=$false,
Position=0)]
[string]$Text = ("{0}/{1}" -f $script:current, $script:total)
)
$params = @{
Activity = $script:ActivityName
Status = $Text
PercentComplete = ($script:current / $script:total * 100)
}
if($script:timeEstimation) {
if($script:current -gt 5) {
$params["SecondsRemaining"] = (((Get-Date) - $script:startTime).TotalSeconds / $script:current) * ($script:total - $script:current)
}
}
Write-Progress @params
if($script:current -lt $script:total) {
$script:current += 1
} else {
Write-Warning "Progressbar incremented too far"
}
}
function Complete() {Write-Progress -Activity $script:ActivityName -Status $script:total -PercentComplete 100 -Completed}
export-modulemember -function setTotal,getTotal,Progress,Complete,setActivityName,enableTimeEstimation,disableTimeEstimation
} -AsCustomObject
# Set initial values
$m.setTotal($TotalCount)
$m.setActivityName($ActivityName)
if($TimeEstimationEnabled) {
$m.enableTimeEstimation()
}
return $m;
}
To use it, simply copy and paste the code into any PowerShell. After doing this, you have a new cmdlet available – New-Progressbar. You can use Get-Help New-ProgressBar -Full to get example usage, but here is a quick example script:
# Get a big list of files of unknown size
$files = @(dir $env:SystemRoot | select -First (Get-Random -Minimum 10 -Maximum 100))
# Create progressbar
$bar = New-Progressbar -TotalCount $files.Count -ActivityName "Processing files"
# Pretend to process files in order to progress the progressbar
$files | foreach {
$bar.Progress($_.Name)
sleep -Milliseconds 100
}
# Complete the progressbar
$bar.Complete()
As you can see, $bar is here a custom class with methods presented through the module. You can have internal methods too; only the methods exported by Export-ModuleMember is available externally.
Hope this helps someone as a quick reference to creating a PowerShell module.