简体   繁体   中英

Handling pipeline and parameter input in a Powershell function

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.

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.

If anyone wishes for a very, very simple explanation of how to read from piped input see

How do you write a powershell function that reads from piped input?

Had this ^ existed when I had this question, I would have saved a lot of time because this thread is quite complicated and doesn't actually explain how to handle pipelined input into a function.

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.

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.

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.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM