Parameters: `param()`

A param() block at the top of a script names its inputs, so one script works on any folder; each parameter can have a type, a default value or be a [switch], Mandatory makes PowerShell ask for a missing one, and Tab completes their names.

Apprentice · PowerShell · Updated

After this lesson you can

  • Give a script named parameters with param(), with types, default values and [switch]
  • Make a parameter Mandatory, and run a script with its parameters by name, by position or with Tab completion

Key takeaways

  • param() at the top of a script names its inputs. .\Backup-Folder.ps1 -Path club puts club into $Path, and one script works on any folder.
  • Values given without names fill the parameters in the order param() lists them. Names are clearer in anything you keep.
  • A default value (= 'backup') is used when a parameter is left out. A type ([int]) turns away a value that doesn't fit before the script starts.
  • A [switch] is on when you write it and off when you don't. [Parameter(Mandatory)] makes PowerShell ask for a value that's missing.
  • Tab, Ctrl+Space and Get-Help know your script's parameters, the same as they know a cmdlet's.

Your homework deserves a backup, and a script can make one every evening. But a script with homework and backup typed into it only ever copies that one folder into that one place. For the chess club's sign-up sheet you'd copy the script and change the names, and next month you'd fix the same mistake in two files. PowerShell's own commands don't work that way: nobody edits Copy-Item to copy a different file. You hand it -Path and -Destination. Your scripts can take parameters too, and then one script works on any folder, the way an app opens any document you give it. Every script that other people run, from an IT team's setup script to a developer's build script, starts with its parameters.

Make this chapter's practice folder: some homework, a club's sign-up sheet and an empty backup folder. These lines are safe to run again:

New-Item -ItemType Directory -Force -Path "$HOME\terminal-practice\smart-backup" | Out-Null
Set-Location "$HOME\terminal-practice\smart-backup"
New-Item -ItemType Directory -Force -Path homework, club, backup | Out-Null
Set-Content -Path homework\essay.txt -Value 'Why the Town Library Matters', 'First draft, two pages.'
Set-Content -Path homework\notes.txt -Value 'Chapter 4: read by Friday', 'Lab report due Monday'
Set-Content -Path homework\reading-list.txt -Value 'Hatchet', 'The Hobbit', 'A Wrinkle in Time'
New-Item -ItemType File -Force -Path homework\empty-draft.txt | Out-Null
Set-Content -Path club\sign-ups.txt -Value 'Ana', 'Ben', 'Chloe'
(Get-Item homework\essay.txt).LastWriteTime = (Get-Date).AddHours(-1)
(Get-Item homework\notes.txt).LastWriteTime = (Get-Date).AddDays(-3)
(Get-Item homework\reading-list.txt).LastWriteTime = (Get-Date).AddDays(-10)
(Get-Item club\sign-ups.txt).LastWriteTime = (Get-Date).AddDays(-20)

The last four lines set each file's date, as if you'd written the essay an hour ago, the notes three days ago, the reading list ten days ago and the sign-up sheet twenty days ago, so your results match the lesson's.

A script's own inputs: param()

Open a new file with code Backup-Folder.ps1 (Writing Scripts in VS Code), type these lines, and save. The name follows PowerShell's own rule, a verb and a noun (Verb-Noun: Names That Explain Themselves); Backup is one of the verbs on Get-Verb's list.

# Backup-Folder.ps1: copies the files in one folder into another.
param(
    $Path,
    $Destination
)
Get-ChildItem -Path $Path -File | Copy-Item -Destination $Destination
"Backed up $Path into $Destination."

param( ) (read "param") is the script's list of inputs, its param block, and it comes first, after any comments. Each name in it is a parameter: you write it with a dash when you run the script, the same as a cmdlet's (Parameters, Values and Switches), and inside the script $Path holds whatever came after -Path. The last line, text on its own, is printed: the short way of writing Write-Output.

.\Backup-Folder.ps1 -Path homework -Destination backup
Get-ChildItem backup -Name
.\Backup-Folder.ps1 homework backup
.\Backup-Folder.ps1 -Path club -Destination backup
Backed up homework into backup.
empty-draft.txt
essay.txt
notes.txt
reading-list.txt
Backed up homework into backup.
Backed up club into backup.

The third line leaves the names out, so the values fill the parameters in the order param() lists them: they're positional. Names are clearer in anything you keep. The last line is the point: the same script, another folder, nothing edited.

Defaults and types

Most evenings you'd type the same names, and copying every file every time wastes time on a big folder. Here's the script grown up. Replace everything in the file with this:

# Backup-Folder.ps1: copies a folder's recent changes into a backup folder.
param(
    [string]$Path = 'homework',
    [string]$Destination = 'backup',
    [int]$Days = 1,
    [switch]$Preview
)
$since = (Get-Date).AddDays(-$Days)
$changed = Get-ChildItem -Path $Path -File | Where-Object LastWriteTime -gt $since
$changed | Copy-Item -Destination $Destination -WhatIf:$Preview
"Days looked back: $Days. Files copied: $($changed.Count)."

= 'homework' is a default value, used when you leave the parameter out. [string] and [int] in front are types (Numbers, Dates and Types). $Days decides how far back to look: only files changed since then pass Where-Object (Filtering: Where-Object and Comparisons).

.\Backup-Folder.ps1
.\Backup-Folder.ps1 -Days 7
.\Backup-Folder.ps1 -Days seven
Days looked back: 1. Files copied: 2.
Days looked back: 7. Files copied: 3.
Backup-Folder.ps1: Cannot process argument transformation on parameter 'Days'. Cannot convert value "seven" to type "System.Int32". Error: "The input string 'seven' was not in a correct format."

One day back finds the essay and the empty draft; seven days adds the notes. The type turned seven away before a single line of the script ran.

On or off: [switch], and Mandatory

[switch]$Preview is a switch parameter: off unless you write -Preview, with no value after it, like -Force. -WhatIf:$Preview hands it on to Copy-Item, read "WhatIf, if Preview is on":

.\Backup-Folder.ps1 -Preview
What if: Performing the operation "Copy File" on target "Item: C:\Users\Sam\terminal-practice\smart-backup\homework\empty-draft.txt Destination: C:\Users\Sam\terminal-practice\smart-backup\backup\empty-draft.txt".
What if: Performing the operation "Copy File" on target "Item: C:\Users\Sam\terminal-practice\smart-backup\homework\essay.txt Destination: C:\Users\Sam\terminal-practice\smart-backup\backup\essay.txt".
Days looked back: 1. Files copied: 2.

Nothing was copied, yet the last line says two files were. The script can't tell the two cases apart yet; If This, Then That: if, elseif and else fixes that.

Some values have no sensible default. Put [Parameter(Mandatory)] (read "mandatory", meaning required) on the line above a parameter, and PowerShell asks for it when it's missing. For a moment, try it on $Destination, with its default taken away:

    [Parameter(Mandatory)]
    [string]$Destination,
cmdlet Backup-Folder.ps1 at command pipeline position 1
Supply values for the following parameters:
Destination: backup
Days looked back: 1. Files copied: 2.

backup is what was typed at the question. A parameter has a default or is Mandatory, not both, so put the default back afterwards: this chapter's script keeps it. Everything here works the same in Windows PowerShell 5.1; on a Mac or Linux, run the script as ./Backup-Folder.ps1.

Tab knows your parameters

Type .\Backup-Folder.ps1 - and press Tab:

.\Backup-Folder.ps1 -Path

Press Tab again for -Destination, then -Days and -Preview, or Ctrl+Space to see all four at once (Keyboard Shortcuts at the Prompt). Get-Help knows them too:

Get-Help .\Backup-Folder.ps1
Backup-Folder.ps1 [[-Path] <string>] [[-Destination] <string>] [[-Days] <int>] [-Preview]

Square brackets mark what you may leave out, and <int> is the type. Your script now explains itself, like a cmdlet.

Try it. Preview a backup of the club folder that looks back 30 days, typing as few letters as you can with Tab. Then run it for real.

Try it in the terminal

Terminal Quest: Mission Control, goal 5: played right on the lesson page.

Your turn at Mission Control: the satellite needs a beacon. Write beacon.ps1, a one-line script whose param($Callsign) takes the call sign and outputs BEACON and the call sign, then pipe it into the transmitter. The goals before it are played for you.

Check yourself

2 questions to try on the lesson page.

So what

A param block turns a script with names typed into it into a tool that takes input like any command: defaults for the usual case, types that turn away nonsense, switches for choices, and Tab to fill it all in.

Key terms

Cheat sheets

Related