Once you have some basic PowerShell experience I think you will begin looking for all sorts of ways to use PowerShell. Although one of the biggest obstacles for many IT Pros is the thought of having to type everything. Certainly, PowerShell has a number of features to mitigate this, often misperceived, burden such as tab completion and aliases. It is the latter that I want to discuss today.
ManageEngine ADManager Plus - Download Free Trial
Exclusive offer on ADManager Plus for US and UK regions. Claim now!
An alias is simply an short command name alternative. Instead of typing Get-Process you can type ps. Even I can't screw that up. Using aliases at the command prompt is a great way to speed up your work and cut down on typos. While I wouldn't type this in a script:
gsv | ? status -eq running
At a prompt, it is quick and I get the desired results. However, you can't create an alias for a command and its parameters. For example, I love the -First and -Last parameters with Select-Object. But I can't create an alias equivalent to Select-Object -last 10. What you can do however, is create your own wrapper around a command and put an alias to that. Here is a function I wrote for Select-Object -Last X.
#requires -version 3.0 Function Select-Last { <# .Synopsis Select last X number of objects .Description This command takes pipelined input and selects the last specified number of objects which are then written to the pipeline. There is a trade off of convenience for performance. For a very large number processed objects, use Select-Object directly .Example PS C:\> dir c:\scripts\*.ps1 | sort lastwritetime | last 5 .Notes Last Updated: 8/25/2014 Version : 0.9 .Link Select-Object #> [cmdletbinding()] Param( [Parameter(Position=0,Mandatory,HelpMessage="How many objects do you want to get?")] [ValidateScript({$_ -gt 0})] [int]$Last, [Parameter(Position=1,Mandatory,ValueFromPipeline)] [object]$InputObject ) Begin { Write-Verbose -Message "Starting $($MyInvocation.Mycommand)" Write-Verbose -Message "Selecting last $Last objects" #initialize an array to hold all incoming objects $data=@() #initialize a counter $total=0 #track when the command started in order to calculate how long the command took to complete. $start=Get-Date } #begin Process { $total++ if ($total -le $Last) { #add each piped object to temporary array $data+=$InputObject } else { #move all items in the array up one $data = $data[1..$Last] #add the new item $data+=$InputObject } } #process End { #write the results $data $end = Get-Date Write-Verbose -Message "Processed $total items in $($end-$start)" Write-Verbose -Message "Ending $($MyInvocation.Mycommand)" } #end } Set-Alias -Name Last -Value Select-Last
You'll notice that the last part of my code snippet is defining an alias called Last that references this function. But now I have something quick and easy to use at a PowerShell prompt.
dir c:\work\*.txt | sort lastwritetime | last 5
You'll have to figure out the best way to wrap the cmdlet. In this case, I am taking each piped in object and adding it to an array, only keeping the specified number of items. As each item is added, the other items are moved "up" in the array.
Be aware that there may be a trade-off between convenience and performance. This command using my alias and custom function:
Measure-command {1..5000 | last 3}
took 237ms. Whereas the Select-Object approach:
Measure-command {1..5000 | select -Last 3}
Only took 49ms. But I bet you might be willing to accept that tradeoff to save some typing. Of course, if there is a Last command there should be a First command.
Function Select-First { <# .Synopsis Select first X number of objects .Description This command takes pipelined input and selects the first specified number of objects which are then written to the pipeline. .Example PS C:\> get-process | sort WS -descending | first 5 .Notes Last Updated: 8/25/2014 Version : 0.9 .Link Select-Object #> [cmdletbinding()] Param( [Parameter(Position=0,Mandatory,HelpMessage="How many objects do you want to get?")] [ValidateScript({$_ -gt 0})] [int]$First, [Parameter(Position=1,Mandatory,ValueFromPipeline)] [object]$InputObject ) Begin { Write-Verbose -Message "Starting $($MyInvocation.Mycommand)" Write-Verbose -Message "Selecting first $First objects" #track when the command started in order to calculate how long the command took to complete. $start=Get-Date #initialize a counter $i=0 } #begin Process { #add each piped object to temporary array $i++ if ($i -le $First) { $InputObject } else { #we're done here Write-Verbose "Limit reached" $end = Get-Date Write-Verbose -Message "Processed $($data.count) items in $($end-$start)" Write-Verbose -Message "Ending $($MyInvocation.Mycommand)" #bail out break } } #process End { #not used } #end } Set-Alias -Name First -Value Select-First
Performance-wise this is easier because all I have to do is count piped in objects and bail out once I reach the limit.
ps | sort ws -des | first 3
In this example I'm not only taking advantage of aliases, but also positional parameters and only having to type enough the of the parameter name so PowerShell knows what I want.
So there are ways to make PowerShell more keyboard friendly, although it might take a little work on your part. Next time we'll look at another alternative.