What is PowerShell splatting and how does it work?
Introduction
PowerShell splatting is an interesting phrase that refers to a technique used to pass parameter values to a PowerShell command. Instead of supplying a long list of parameters, splatting allows you to use PowerShell objects containing parameter values instead. This helps make your code easier to read and allows you to reuse parameter values between commands more efficiently.
In this tutorial, you’ll learn everything there is to know about PowerShell splatting to help you write more awesome code!
Prerequisites
To follow along with this tutorial, you’ll need to have PowerShell 7.1 installed. Splatting has been around since PowerShell 2.0, but some of the techniques described in this article, such as overriding splatted parameters, have only been available since version 7.1.
What is PowerShell splatting?
First, you’ll need to be familiar with the basic named and positional PowerShell parameter types. For named parameters, you provide both a parameter name and value to a command; for example:
# Copy a text file using the Path and Destination named parameters.
Copy-Item -Path ".\source.txt" -Destination ".\dest.txt"
For positional parameters, you simply provide the parameter values in the correct order, as you’ll see below. The required order is determined by the command itself.
# This works because Copy-Item accepts Path as the first parameter and Destination as the second.
# Other commands will be configured differently.
Copy-Item ".\source.txt" ".\dest.txt"
These two methods are fine, but can quickly become difficult to deal with once you have many parameters for a command. This is where splatting comes in.
Splatting named parameters using hashtables
To use splatting with named parameters, you first create a hashtable containing key/value pairs for each parameter/value. Then, you pass this variable to the command, replacing the dollar symbol ($
) in the name with an At symbol (@
); for example:
# Create a hashtable containing the parameters and values.
$Params = @{
Path = ".\source.txt"
Destination = ".\dest.txt"
}
# Splat the parameters to the Copy-Item command using the hashtable name with the @ symbol.
Copy-Item @Params
Splatting positional parameters using arrays
To use splatting with positional parameters, you create an array containing each parameter value in the correct order and pass this to the command, in the same way as above; for example:
# Create an array containing each parameter value in the correct ordered position.
$Params = @(
".\source.txt",
".\dest.txt"
)
# Splat the parameter values to the Copy-Item command using the array name with the @ symbol.
Copy-Item @Params
Combining explicit and splatted parameters
It’s often useful to combine parameter splatting with explicitly defined parameters. For example, you might want to include the WhatIf
switch parameter to test a copy operation before actually copying the files, as you’ll see below.
# Create a hashtable containing the parameters and values.
$Params = @{
Path = ".\source.txt"
Destination = ".\dest.txt"
}
# Splat the $Params hashtable to the Copy-Item command and also specify the WhatIf parameter explicitly.
Copy-Item @Params -WhatIf
If you wanted to splat the WhatIf
parameter, you’d need to set the value to $true
in the splatting hashtable or array.
Overriding splatted parameters
In the previous example, you combined splatted and explicitly defined parameters. With the release of PowerShell 7.1, you can now override a splatted parameter in a similar way; for example:
# Create a hashtable containing the parameters and values.
$Params = @{
Path = ".\source.txt"
Destination = ".\dest.txt"
WhatIf = $true
}
# Splat the $Params hashtable to the Copy-Item command and override the WhatIf parameter.
Copy-Item @Params -WhatIf:$false
In previous versions of PowerShell this will throw an error complaining that the WhatIf
parameter is specified more than once.
Reusing splatted parameters
Another useful application of splatting is to define a set of parameters once, and then reuse them in subsequent commands; for example:
# Create a hashtable containing the parameters and values to reuse.
$Params = @{
Path = ".\source\"
Force = $true
Include = "*.txt"
}
# Copy all txt files in the .\source\ directory to .\destination-one\
Copy-Item @Params -Destination ".\destination-one\"
# Reuse the splatting hashtable to copy all txt files in the .\source\ directory to .\destination-two\.
# It doesn't matter which position you put the splatting hashtable.
Copy-Item -Destination ".\destination-two\" @Params
Forwarding parameters using $PSBoundParameters
The $PSBoundParameters
variable is an automatic variable, which contains a dictionary of the parameters (and values) passed to a script or function. In the example below, you’ll see how you can use splatting to forward the parameters from one function (Invoke-FuncTwo
) to another (Invoke-FuncOne
) using the $PSBoundParameters
variable. This is an advanced technique and can take some getting your head around, but it does open up some interesting possibilities once you understand it.
function Invoke-FuncOne {
param ($a, $b, $c)
$Output = ""
if ($a) {$Output += $a}
if ($b) {$Output += $b}
if ($c) {$Output += $c}
Write-Output $Output
}
function Invoke-FuncTwo {
param ($a, $b, $c)
# Call the Invoke-FuncOne function and pass the $a, $b, and $c parameters
# by splatting the @PSBoundParameters variable.
Invoke-FuncOne @PSBoundParameters
# Call the Invoke-FuncOne function again, this time with the $a and $c
# parameters, but not with $b.
$LimitedParameters = $PSBoundParameters
$LimitedParameters.Remove("b") | Out-Null
Invoke-FuncOne @LimitedParameters
}
PS C:\> Invoke-FuncTwo -a "Hello" -b "World" -c "!"
HelloWorld!
Hello!
Using splatting with proxy functions
Proxy functions are another advanced PowerShell technique. They’re essentially a wrapper for an existing command, allowing you to add functionality not already implemented. Using the $Args
automatic variable, which contains all unassigned parameters (i.e., parameters that are not named in the param
block), you can pass parameters to a proxy function and then use splatting to pass them onto the existing command; for example:
# Define a function to simply pass all parameters to the Get-Process command by
# splatting the @Args variable.
function Get-ProcessProxy {
Get-Process @Args
}
# Call the proxy function and specify a parameter called Name.
Get-ProcessProxy -Name "powershell"
PS C:\> Get-ProcessProxy -Name "powershell"
NPM(K) PM(M) WS(M) CPU(s) Id SI ProcessName
------ ----- ----- ------ -- -- -----------
25 56.82 50.62 0.00 14832 0 powershell
26 56.70 24.09 0.00 21068 0 powershell
You can also use the $Args
variable combined with named parameters (i.e., those in the param
block of a function):
# Define a function with named parameters $a and $b that also passes any other parameters
# to Get-Process by splatting the @Args variable.
function Get-ProcessProxy {
param (
[bool] $a,
[string] $b
)
if ($a) {Get-Process @Args}
if ($b) {Write-Output $b}
}
# When $a is true, Get-Process will be called and the Name parameter will be passed.
Get-ProcessProxy -Name "powershell" -a:$true
# When $b is specified, its value will be printed to the console using Write-Output.
Get-ProcessProxy -Name "powershell" -b "Nope, not today!"
PS C:\> Get-ProcessProxy -Name "powershell" -a:$true
NPM(K) PM(M) WS(M) CPU(s) Id SI ProcessName
------ ----- ----- ------ -- -- -----------
25 56.82 44.68 0.00 14832 0 powershell
26 56.76 23.83 0.00 21068 0 powershell
25 56.85 27.45 0.00 22684 0 powershell
26 57.57 22.76 0.00 23800 0 powershell
25 56.91 23.14 0.00 31348 0 powershell
25 57.19 23.48 0.00 37496 0 powershell
25 57.61 27.57 0.00 39124 0 powershell
25 57.56 25.48 0.00 41688 0 powershell
25 57.09 23.49 0.00 47316 0 powershell
PS C:\> Get-ProcessProxy -Name "powershell" -b "Nope, not today!"
Nope, not today!
The $Args
variable will not contain any of the named parameters.
Why not just use backticks?
There is one other technique that you might come across: namely, the use of backticks `
to break the command line, so that code becomes more readable; for example:
Copy-Item -Path ".\source\" `
-Destination ".\dest\" `
-Include "*.txt" `
-Force
This is widely seen as a bad choice. Backticks are hard to read, easy to miss, and easy to mistype. Also, if you accidentally enter a space after a backtick, the command will fail with an error, as you’ll see below.
PS C:\> Copy-Item -Path ".\source\" `
>> -Destination ".\dest\" `
>> -Include "*.txt" `
>> -Force
#error
Copy-Item: Cannot find path 'C:\source\' because it does not exist.
-Destination:
Line |
2 | -Destination ".\dest\" `
| ~~~~~~~~~~~~
| The term '-Destination' is not recognized as a name of a cmdlet, function, script file, or executable program.
Check the spelling of the name, or if a path was included, verify that the path is correct and try again.
#enderror
This causes unnecessary confusion and can be hard to debug given that the error message is not usually very helpful. The bottom line is that splatting is a much better option.
Conclusion
In this tutorial, you’ve worked through examples of all the different splatting techniques. Learning how to combine these techniques gives you plenty of options to simplify complex code and make it easier to read. So, make sure your next scripts use splatting and keep writing awesome code!