Advanced Functions: `[CmdletBinding()]`
One line, [CmdletBinding()] above param(), turns a function into an advanced function that binds its parameters the way a cmdlet does: it refuses a misspelled parameter instead of quietly ignoring it, and gets -Verbose, -ErrorAction and the other common parameters for free.
Journeyman · PowerShell · Updated
After this lesson you can
- Make a function advanced with [CmdletBinding()], so it gets -Verbose, -ErrorAction and the other common parameters
- Predict how a plain function and an advanced function each handle a misspelled parameter, -Verbose and -ErrorAction
Key takeaways
- A plain function quietly ignores a parameter it doesn't know, -Verbose and -ErrorAction included.
- [CmdletBinding()] above param() makes an advanced function: a misspelled parameter is an error, as it is for a cmdlet.
- An advanced function gets the common parameters for free. -Verbose shows its Write-Verbose lines, and -ErrorAction reaches the commands inside it.
- Get-Command -Syntax shows [<CommonParameters>] at the end when a function is advanced.
Your Get-Song from Functions: Naming a Job works when you type it right. Tools get used by people in a hurry, though, and someone will type Get-Song -Genre Focus when the parameter is called -Mood. A cmdlet would tell them. Your function doesn't, and that's how small mistakes turn into wrong answers nobody notices: a report built from the wrong songs, a cleanup that skipped the folder it was meant for. This lesson adds the one line that makes your function as strict, and as helpful, as a cmdlet.
You need the practice folder and PlaylistTools.ps1 from Functions: Naming a Job. Go to the folder and load the file:
Set-Location "$HOME\terminal-practice\playlist-tools"
. .\PlaylistTools.ps1
A plain function lets mistakes slide
Try the misspelled parameter:
Get-Song -Genre Focus
Title Artist Minutes Mood
----- ------ ------- ----
Mile Marker 9 Juniper Kite 4 Upbeat
Gas Station Coffee The Night Owls 3 Upbeat
Rainy Overpass Juniper Kite 6 Chill
Backseat Snacks Maple Street 3 Upbeat
Cruise Control The Night Owls 5 Chill
Left at the Lighthouse Maple Street 7 Focus
Last Exit Home Juniper Kite 4 Chill
No error, and every song instead of the one Focus song. A plain function puts any parameter it doesn't know on a leftover list and does nothing with it. The parameters every cmdlet has go the same way. Ask it to keep quiet about a missing playlist:
Get-Song -Path rock-trip.csv -ErrorAction SilentlyContinue
Import-Csv: C:\Users\Sam\terminal-practice\playlist-tools\PlaylistTools.ps1:6
Line |
6 | $songs = Import-Csv -Path $Path
| ~~~~~~~~~~~~~~~~~~~~~~
| Could not find file 'C:\Users\Sam\terminal-practice\playlist-tools\rock-trip.csv'.
The red text comes anyway: -ErrorAction was ignored too.
One line: [CmdletBinding()]
Open PlaylistTools.ps1 and add [CmdletBinding()] (read "cmdlet binding") on the line above param(). While you're there, add two Write-Verbose lines that say what the function is doing:
function Get-Song {
[CmdletBinding()]
param(
[string]$Mood,
[string]$Path = 'road-trip.csv'
)
Write-Verbose "Reading songs from $Path"
$songs = Import-Csv -Path $Path
if ($Mood) {
Write-Verbose "Keeping only the $Mood songs"
$songs = $songs | Where-Object Mood -eq $Mood
}
$songs
}
Save, load the file again, and repeat the mistake:
. .\PlaylistTools.ps1
Get-Song -Genre Focus
Get-Song: A parameter cannot be found that matches parameter name 'Genre'.
A function with [CmdletBinding()] is an advanced function: PowerShell matches its parameters the way it does a cmdlet's, and a name it doesn't know is an error. Any [Parameter()] line, such as [Parameter(Mandatory)], makes a function advanced too. You've seen the same line at the top of a script in Where Output Goes: Write-Output, Write-Host and Friends: it works the same way in a script.
The common parameters, for free
Ask Get-Command how to call Get-Song now:
Get-Command Get-Song -Syntax
Get-Song [[-Mood] <string>] [[-Path] <string>] [<CommonParameters>]
[<CommonParameters>] at the end is new. The common parameters are the ones every cmdlet has: -Verbose, -ErrorAction, -WarningAction, -ErrorVariable, -OutVariable and a few more. Once help is installed (Reading Help: Get-Help), Get-Help about_CommonParameters lists them all. Two of them matter most for your tools. -Verbose shows your Write-Verbose lines, which stay hidden otherwise:
Get-Song -Mood Focus -Verbose
VERBOSE: Reading songs from road-trip.csv
VERBOSE: Keeping only the Focus songs
Title Artist Minutes Mood
----- ------ ------- ----
Left at the Lighthouse Maple Street 7 Focus
And -ErrorAction now reaches the commands inside your function, so Import-Csv keeps quiet when you ask it to. The error is still kept in $Error (Reading Errors, and -ErrorAction):
Get-Song -Path rock-trip.csv -ErrorAction SilentlyContinue
$Error[0].Exception.Message
Could not find file 'C:\Users\Sam\terminal-practice\playlist-tools\rock-trip.csv'.
-ErrorAction Stop reaches inside the same way, so a try and catch around your tool sees its errors (try, catch and finally). All of this works the same in Windows PowerShell 5.1 and on a Mac or Linux.
Try it. Run Get-Song -Mood Chill -Verbose, then Get-Song -Moods Chill. What does the second one say, and what would the plain function from Functions: Naming a Job have done with it?
Check yourself
2 questions to try on the lesson page.
So what
Every function you share should start with [CmdletBinding()]: it costs one line, catches mistakes a plain function hides, and gives your tool the switches people already know from every cmdlet.