Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

I don't understand param binding on functions with begin/process/end blocks

Tags:

powershell

function Format-File {
  param(
    [Parameter(Mandatory = $true, Position = 0)]
    [ValidateNotNullOrEmpty()]
    [string] $path=$(throw "path is mandatory ($($MyInvocation.MyCommand))"),

    [Parameter(Mandatory = $true, Position = 1, ValueFromPipelineByPropertyName = $true)]
    [ValidateNotNullOrEmpty()]
    [string] $key,

    [Parameter(Mandatory = $true, Position = 2, ValueFromPipelineByPropertyName = $true)]
    [ValidateNotNullOrEmpty()]
    [string] $value
  )
}

I'm calling it like so, assume I've added values to the dictionary (removed for brevity here)

$dict = New-Object 'System.Collections.Generic.Dictionary[string,string]'
$dict.GetEnumerator() | Format-File -Path $updatePath

Here's my conundrum.

The above works perfectly. However, the following does not, note the difference in the key/value parameter

function Format-File {
  param(
    [Parameter(Mandatory = $true, Position = 0)]
    [ValidateNotNullOrEmpty()]
    [string] $path=$(throw "path is mandatory ($($MyInvocation.MyCommand))"),

    [Parameter(Mandatory = $true, Position = 1, ValueFromPipelineByPropertyName = $true)]
    [ValidateNotNullOrEmpty()]
    [string] $key=$(throw "key is mandatory ($($MyInvocation.MyCommand))"),

    [Parameter(Mandatory = $true, Position = 2, ValueFromPipelineByPropertyName = $true)]
    [ValidateNotNullOrEmpty()]
    [string] $value=$(throw "value is mandatory ($($MyInvocation.MyCommand))")
  )
}

The above throws an exception. It appears to be getting the default value when the function is first called, but when processing, the key/value parameters are set properly.

It makes a bit of sense as to why the key/value wouldn't be set at the time of the function call, but this also means my mental model is off.

So my question is two-fold.

  1. What is the parameter binding process for functions of this nature, and
  2. How does one verify the input of values that have come in from the pipeline? Manually check in the begin block, or is there another method?

If you have links to describe this all in greater detail, I'm happy to read up on it. It just made me realize my mental model of the process is flawed and I'm hoping to fix that.

like image 298
Fred Avatar asked May 01 '15 15:05

Fred


1 Answers

What is the parameter binding process for functions of this nature?

In the Begin block, pipeline bound parameters will be $null or use their default value if there is one. This makes some sense, considering that the pipelining of values hasn't started yet.

In the Process block, the parameter will be the current item in the pipeline.

In the End block, the parameter will be the last value from the Process block, unless there was an exception in validating the parameter, in which case it will use the default (or $null).

How does one verify the input of values that have come in from the pipeline?

You can't check in the Begin block.

The best way is to use [Validate attributes, as you have with [ValidateNotNullOrEmpty()].

Your examples with using throw as a default value are useful in some situations but they are a clever workaround. The thing is, you don't need them since you already declared the parameter as Mandatory.

Instead of using a default value, you can use [ValidateScript( { $value -eq 'MyString' } )] for example.

Since the error message from [ValidateScript()] sucks, you can combine the techniques:

function Format-File {
  param(
    [Parameter(Mandatory = $true, Position = 1, ValueFromPipelineByPropertyName = $true)]
    [ValidateNotNullOrEmpty()]
    [ValidateScript( {
        ($_.Length -le 10) -or $(throw "My custom exception message")
    } )]
    [string] $key
  )
}

Using [ValidateScript()] works whether it's a pipeline parameter or not.

like image 165
briantist Avatar answered Sep 19 '22 12:09

briantist