The Trick to Advanced Functions' Input
One PowerShell v2 feature I've written about before, advanced functions (some people call 'em "script cmdlets"), have a trick about them. Consider a situation where you want a function to accept pipeline input and have it bound to a specific parameter. You also want someone to be able to specify one or more values directly on the parameter.
June 18, 2010
One PowerShell v2 feature I've written about before, advanced functions (some people call 'em "script cmdlets"), have a trick about them. Consider a situation where you want a function to accept pipeline input and have it bound to a specific parameter. You also want someone to be able to specify one or more values directly on the parameter. For example, you have a function named Do-Something, and you want it to have an -inputObject parameter that accepts one or more strings, and you want to be able to use it in all of these ways:
# Pipeline input ByValue
"one","two","three" | Do-Something
# Parameter direct, single value
Do-Something -inputObject "one"
# Parameter direct, multiple values
Do-Something -inputObject (Get-Content items.txt)
This is actually very tricky. You have to declare the parameter as a type [string[]] so that it'll accept multiple values. In the first use case, the function's PROCESS script block will automatically enumerate the pipeline input, so that you don't have to. When using the parameter directly, though, the parameter will contain not one object at a time, but rather ALL of the objects in an array - so you have to enumerate that, or "unravel" it, yourself.
Fellow PowerShell MVP Kirk Munro (also of PowerGUI.org fame) helped me figure this one out. Here's his solution (modified a bit by myself):
function Do-Something {
[CmdletBinding()]
param(
[Parameter(Mandatory=$true,Position=0,ValueFromPipeline=$true)]
[ValidateNotNullOrEmpty()]
[System.Management.Automation.PSObject]
$InputObject
)
begin {
$pipelineInput = -not $PSBoundParameters.ContainsKey('InputObject')
$index = 1
}
process {
if ($pipelineInput) {
# Use $inputObject
} else {
foreach ($item in $InputObject) {
# Use $item
}
}
}
}
The two comments indicate what you'd do in order to always work with one input object at a time, regardless of how it was provided. You would probably implement your actual "do whatever" code in a different function, so that you could just call it from either of the two locations.
Thanks, Kirk!
There are lots more PowerShell FAQs and tips - for PowerShell users of all skill levels - on my PowerShell home page.
About the Author
You May Also Like