1

How can I pass values to my functions in the same way the PowerShell cmdlet Write-Output can be passed multiple values and process them as expected?

'one', 'two' | Write-Output

which will product the following output

one
two

I thought this was a way to do that, but it isn't:

Function ProcessNames ([Parameter(ValueFromPipeline=$true)][string[]]$Names) {
  Foreach ( $name in $Names ) {
    # do something
  }
}
'bob', 'alice' | ProcessNames

In the above example, only the last element in the list gets processed - in this case 'alice'.

What am I doing wrong?

2 Answers 2

3

You have built your function in the simplified style. Functions have a BEGIN, PROCESS, and END block, of which only the END block runs by default. You can fix it by declaring that you want to operate in the PROCESS scriptblock like this:

Function ProcessNames ([Parameter(ValueFromPipeline=$true)][string[]]$Names) {
  PROCESS{
      Foreach ( $name in $Names ) {
        $name
      }
  }
}
'bob', 'alice' | ProcessNames
Sign up to request clarification or add additional context in comments.

Comments

0

To add to TheMadTechnician's helpful answer:

  • In addition to using a process block, it's better to also use a param(...) block for parameter declarations in advanced functions - which your function implicitly is, due to use of a [Parameter()] attribute.

    • Only a param(...) block allows you to use the [CmdletBinding()] attribute, which modifies the behavior of advanced functions - see this answer.
  • If your intent is to support processing multiple input objects via the pipeline only, you needn't declare your pipeline-binding parameter as an array.

    • In fact, the majority of cmdlets work this way - they do not meaningfully support processing an array of input objects passed to the pipeline-binding parameter directly, as an argument - see GitHub issue #4242 for a discussion.

    • That said, Write-Output, Out-File and Set-Content are notable exceptions; e.g., the following commands are equivalent to providing 1, 2, 3 via the pipeline:
      Write-Output -InputObject 1, 2, 3
      Out-File out.txt -InputObject 1, 2, 3
      Set-Content out.txt -Value 1, 2, 3


Supporting multiple input objects via the pipeline only simplifies - and speeds up - things:

function ProcessNames {
  param(
    [Parameter(ValueFromPipeline)]
    [string] $Name # Defined as a *single* string
  ) 
  process {
    # Do something with $Name
    $Name + '!' # sample operation
  }
}

'bob', 'alice' | ProcessNames then yields the equivalent of: 'bob!', 'alice!'


If pipeline input is all you need to support - not also arguments - a simple alternative is to use a filter, which is a simplified version of a function that (a) has no parameters and (b) whose body implicitly runs inside a process block and receives pipeline input via automatic $_ variable (aka $PSItem):

filter ProcessNames {
  # Do something with $_
  $_ + '!' # sample operation
}

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.