How do I have an array parameter that takes input from the args or the pipeline in Powershell? How do I have an array parameter that takes input from the args or the pipeline in Powershell? powershell powershell

How do I have an array parameter that takes input from the args or the pipeline in Powershell?


Use the automatic variable $input.

If only pipeline input is expected then:

function my-function {    $arg = @($input)    $arg}

But I often use this combined approach (a function that accepts input both as an argument or via pipeline):

function my-function {    param([string[]]$arg)    # if $arg is $null assume data are piped    if ($null -eq $arg) {        $arg = @($input)    }    $arg}# testmy-function 1,2,3,41,2,3,4 | my-function


Here's another example using Powershell 2.0+

This example is if the parameter is not required:

function my-function {  [cmdletbinding()]  Param(    [Parameter(ValueFromPipeline=$True)]    [string[]]$Names  )  End {    # Verify pipe by Counting input    $list = @($input)    $Names = if($list.Count) { $list }       elseif(!$Names) { @(<InsertDefaultValueHere>) }       else { @($Names) }    $Names -join ':'  }}

There's one case where it would error out without the 'elseif'. If no value was supplied for Names, then $Names variable will not exist and there'd be problems. See this link for explanation.

If it is required, then it doesn't have to be as complicated.

function my-function {  [cmdletbinding()]  Param(    [Parameter(Mandatory=$true,ValueFromPipeline=$True)]    [string[]]$Names  )  End {    # Verify pipe by Counting input    $list = @($input)    if($list.Count) { $Names = $list }     $Names -join ':'  }}

It works, exactly as expected and I now I always reference that link when writing my Piped Functions.


ValueFromPipeline

You should use the pipeline (ValueFromPipeline) as PowerShell is specially designed for it.

$args

First of all, there is no real difference between:
my-function -<ParamName> 1,2,3,4 and
my-function 1,2,3,4 (assuming that the parameter $ParamName is at the first position).

The point is that the parameter name $args is just an unfortunate choice as $args is an automatic variable and therefore shouldn't be used for a parameter name. Almost any other name (that is not in the automatic variables list) should do as in the example from Sean M., but instead you should implement your cmdlet assuming that it will be called from the middle of a pipeline (see: Strongly Encouraged Development Guidelines).
(And if you want to do this completely right, you should give a singular name, plural parameter names should be used only in those cases where the value of the parameter is always a multiple-element value.)

Middle

The supposed cmdlet in your question is not a very good example as it only cares about the input and has a single output therefore I have created another example:

Function Create-Object {    Param([Parameter(ValueFromPipeline=$true)][String[]]$Name)    Begin {        $Batch = 0        $Index = 0    }    Process {        $Batch++        $Name | ForEach {            $Index++            [PSCustomObject]@{'Name' = $_; 'Index' = $Index; 'Batch' = $Batch}        }    }}

It basically creates custom objects out of a list of names ($Names = "Adam", "Ben", "Carry").
This happens when you supply the '$Names` via an argument:

Create-Object $NamesName  Index Batch----  ----- -----Adam      1     1Ben       2     1Carry     3     1

(It iterates through all the names in $Name parameter using the ForEach cmdlet.)

And this happens when you supply the $Names via the pipeline:

$Names | Create-ObjectName  Index Batch----  ----- -----Adam      1     1Ben       2     2Carry     3     3

Note that the output is quiet similar (if it wasn't for the batch column, the output is in fact the same) but the objects are now created in 3 separate batches meaning that every item is iterated at the process method and the ForEach loop only iterates ones every batch because the $Name parameter contains an array with one single item each process iteration.

Use case

Imaging that the $Names come from a slow source (e.g. a different threat, or a remote database). In the case you using the pipeline for processing the $Names your cmdlet can start processing the $Names (and pass the new objects onto the next cmdlet) even if not all $Names are available yet. In comparison to providing the $Names via an argument were all the $Names will need to be collected first before your cmdlet will process them and pass the new objects onto the pipeline.