Home > Net >  Perform functions default action, unless pipeline input was given?
Perform functions default action, unless pipeline input was given?

Time:11-07

Lets say I have a function like this:

Function Hello(){
    Write-Host "Hello, World"}

when used on its own it works perfect but I would also like for it to receive pipeline input:

$MyString = "Something Else please"
$MyString | Hello

In the second example, Something Else please should be printed instead of the default value of Hello, World.

I have searched and searched and have turned up empty handed. everything out there keeps mentioning default parameters. I am not looking to use parameters.

Someone please point me in the right direction.

CodePudding user response:

Declare a parameter, and:

  • assign it a default value.

  • make it pipeline-binding, and be sure to process each pipeline input object in the function body, namely via a process block

    • Note: Declaring a parameter pipeline-binding (with [Parameter(ValueFromPipeline)] below) implicitly makes a function an advanced one, which has (generally beneficial) behavioral implications - see this answer.
function Hello {
  param(
    [Parameter(ValueFromPipeline)]
    $InputObject = 'Hello, World'   # default value
  )
  
  process {
    # Called for each pipeline input object, or once with the default value.
    Write-Host $InputObject
  }

}

For robustness, it's generally preferable to declare parameters explicitly, as shown above.


The - less desirable - parameter-less, simple-function alternative is the following, which collects all pipeline input up front, as it too implicitly runs in an end block, and uses the automatic $input variable and $MyInvocation.ExpectingInput to detect if pipeline input was provided, as suggested by Santiago Squarzon:

function Hello {

  if ($MyInvocation.ExpectingInput) { # input from the pipeline
    $input | Write-Host  # pass each pipeline input object to Write-Host
  } else { # no pipeline input -> use default value
    'Hello, World' | Write-Host
  }

}

As for what you tried in your answer:

  • By not using a process block, in effect only the last input object from the pipeline is bound to parameter $InputObject, because a function body without (any one of) begin, process and end blocks implicitly runs in an end block, i.e. after all pipeline input has been received.

  • Generally, there's no good reason to type a parameter [PSObject] or [PSObject[]], given that [psobject] is a usually invisible helper type used behind the scenes.

    • Not typing a parameter is the same as typing it [object], which is what should be used to accept arguments of any type.

    • Typing it [array] is the same as typing it [object[]], but note that if you type a pipeline-binding parameter as an array, each individual input object is automatically converted to an array - which slows down processing.

      • Only if you need to accept multiple values as a single command-line argument (as opposed to via the pipeline) is declaring a parameter as an array warranted - and when you do, you then need to enumerate each bound parameter value in your process block, as it may itself be a true array.

      • As an aside: That pipeline-binding parameters declared with scalar types only accept multiple values via the pipeline, but not also implicitly as an argument is the subject of GitHub issue #4242

CodePudding user response:

By using this syntax for you function you can create what I think you're after.

Clear-Host

Function Hello {

  Param (
    [Parameter(Mandatory=$False,ValueFromPipeline=$True)]
      [String] $MyText = "Hello"
  )
    Write-Host "$MyText, World"
}

Hello

"It's A Wonderful" | Hello

Output:

Hello, World
It's A Wonderful, World
PS> 

CodePudding user response:

Here is what I arrived at after @Santiago Squarzon pointed me to the right direction

Function Hello {
    Param (
      [Parameter(Mandatory=$False,ValueFromPipeline=$True)]
      [PSObject[]] $InputObject,
    )
    if ($PSCmdlet.MyInvocation.ExpectingInput) {
        "Data received from pipeline input: '$($InputObject)'"
    }
 
    else {
        Write-host "Hello World"
    }
  }
  • Related