Reputation: 121
I'm confused about something I saw in the book Learn PowerShell in a Month of lunches. In chapter 21 when the author discusses functions that accept input via parameter binding or the pipeline he gives two patterns.
The first as follows
function someworkerfunction {
# do some work
}
function Get-SomeWork {
param ([string[]]$computername)
BEGIN {
$usedParameter = $False
if($PSBoundParameters.ContainsKey('computername')) {
$usedParameter = $True
}
}
PROCESS {
if($usedParameter) {
foreach($computer in $computername) {
someworkerfunction -computername $comptuer
}
} else {
someworkerfunction -comptuername $_
}
}
END {}
}
The second like this
function someworkerfunction {
# do stuff
}
function Get-Work {
[CmdletBinding()]
param(
[Parameter(Mandatory=$True,
ValueFromPipelineByPropertyName=$True)]
[Alias('host')]
[string[]]$computername
)
BEGIN {}
PROCESS {
foreach($computer in $computername) {
someworkerfunction -comptuername $computer
}
}
END {}
}
I know the second sample is a standard Powershell 2.0 Advanced function. My question is with Powershell 2.0 support for the cmdletbinding directive would you ever want to use the first pattern. Is that just a legacy from Powershell 1.0? Basically is there ever a time when using Powershell 2.0 that I would want to mess around with the first pattern, when the second pattern is so much cleaner.
Any insight would be appreciated.
Thank you.
Upvotes: 8
Views: 18758
Reputation: 417
No, the first example is not just legacy. In order to create a PowerShell function that uses an array parameter and takes pipeline input you have to do some work.
I will even go as far as to say that the second example does not work. At least I could not get it to work.
Take this example...
function PipelineMadness()
{
[cmdletbinding()]
param (
[Parameter(Mandatory = $true, ValueFromPipeline=$true)]
[int[]] $InputArray
)
Write-Host ('$InputArray.Count {0}' -f $InputArray.Count)
Write-Host $InputArray
Write-Host ('$input.Count {0}' -f $input.Count)
Write-Host $input
if($input) { Write-Host "input is true" }
else { Write-Host "input is false" }
}
results ...
PS C:\Windows\system32> 1..5 | PipelineMadness
$InputArray.Count 1
5
$input.Count 5
1 2 3 4 5
input is true
PS C:\Windows\system32> PipelineMadness (1..5)
$InputArray.Count 5
1 2 3 4 5
$input.Count 1
input is false
Notice that when the pipeline is used the $InputArray
variable is a single value of 5...
Now with BEGIN and PROCESS blocks
function PipelineMadnessProcess()
{
[cmdletbinding()]
param (
[Parameter(Mandatory = $true, ValueFromPipeline=$true)]
[int[]] $InputArray
)
BEGIN
{
Write-Host 'BEGIN'
Write-Host ('$InputArray.Count {0}' -f $InputArray.Count)
Write-Host $InputArray
Write-Host ('$input.Count {0}' -f $input.Count)
Write-Host $input
if($input) { Write-Host "input is true" }
else { Write-Host "input is false" }
}
PROCESS
{
Write-Host 'PROCESS'
Write-Host ('$InputArray.Count {0}' -f $InputArray.Count)
Write-Host $InputArray
Write-Host ('$input.Count {0}' -f $input.Count)
Write-Host $input
if($input) { Write-Host "input is true" }
else { Write-Host "input is false" }
}
}
Now this is where it gets weird
PS C:\Windows\system32> 1..5 | PipelineMadnessProcess
BEGIN
$InputArray.Count 0
$input.Count 0
input is false
PROCESS
$InputArray.Count 1
1
$input.Count 1
1
input is true
PROCESS
$InputArray.Count 1
2
$input.Count 1
2
input is true
...
PROCESS
$InputArray.Count 1
5
$input.Count 1
5
input is true
The BEGIN block does not have any data in there at all. And the process block works well however if you had a foreach
like the example it would actually work but it would be running the foreach
with 1 entry X times. Or if you passed in the array it would run the foreach
once with the full set.
So I guess technically the example would work but it may not work the way you expect it to.
Also note that even though the BEGIN block had no data the function passed syntax validation.
Upvotes: 5
Reputation: 72610
To answer your question, I would say that the first pattern is just a legacy from PowerShell 1.0, you also can use $input
in classical functions without Process script block. As far as you are just writting code for PowerShell 2.0 you can forget it.
Regarding pipeline functions, in powerShell V1.0 they can be handled with filters
.
you just have to know that it have been done like that when you take samples from the Net or when you have to debug old Powerhell code.
Personally I still use old functions
and filters
inside my modules I reserve cmdletbinding
for export functions or profile functions.
Powershell is a bit like lego blocks, you can do many things in many different ways.
Upvotes: 1
Reputation: 68243
The first form is expecting one or more computer names as string arguments either from an argumentlist or from the pipeline.
The second form is expecting either an array of string arguments from an argument list, or input objects from the pipeline that have the computer names as a property.
Upvotes: 0
Reputation: 52567
If you want to process pipeline input in your function but don't want to add all the parameter attributes or want backwards compatibility go with the cmdletbinding
less way.
If you want to use the additional features of PowerShell script cmdlets like the parameter attributes, parameter sets etc... then go with the second one.
Upvotes: 3