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.
- Note: Declaring a parameter pipeline-binding (with
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
andend
blocks implicitly runs in anend
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"
}
}