powershellmodulepowershell-4.0powershell-module

Scriptblock does not get pipeline variable binding when passed as argument to module function


I'm trying to put this function:

function Test-Any {
    [CmdletBinding()]
    param($EvaluateCondition,
        [Parameter(ValueFromPipeline = $true)] $ObjectToTest)
    begin {
        $any = $false
    }
    process {
        if (-not $any -and (& $EvaluateCondition $ObjectToTest)) {
            $any = $true
        }
    }
    end {
        $any
    }
}

into a module. I just created a new module, the my-scripts.psm1 file, which contains just the above function and import it with Import-Module <absolute path>.

The problem is that if I use the function from the module 1..4 | Test-Any { $_ -gt 3 } returns false, because $_ is not set to the value from the pipe.

If I define the function normally in a script and use it from there it works as expected (with $_ getting assigned the integer values).

This happens with PowerShell v4.0 under Windows 7.


Solution

  • The answer of user4003407 did solve my problem, but I don't find the examples very intuitive. So I'm writing this solution mainly to add an additional example.

    I had the following function defined in my MyHelpers.psm1 file which was causing issues:

    function ConvertTo-HashTable {
      param (
        [Parameter(Mandatory, ValueFromPipeline)]
        $InputObject,
        [scriptblock] $Key = { $InputObject },
        [scriptblock] $Value = { $InputObject }
      )
    
      begin { $HashTable = @{} }
      process { $HashTable.Add((& $Key), (& $Value)) }
      end { $HashTable }
    }
    

    On usage:

    Import-Module 'ExchangeOnlineManagement'
    Import-Module 'MyHelpers'
    
    $UserLookup = Get-User | ConvertTo-HashTable -Key { $_.Sid }
    

    I got the error:

    EXCEPTION: Exception calling "Add" with "2" argument(s): "Key cannot be null. (Parameter 'key')"

    Because $_ was not bound correctly, thus returning $null from the scriptblock.

    To fix this I used ForEach-Object to correctly bind $_, as suggested by the answer of user4003407. So I changed the definition to:

    function ConvertTo-HashTable {
      param (
        [Parameter(Mandatory, ValueFromPipeline)]
        $InputObject,
        [scriptblock] $Key = { $InputObject },
        [scriptblock] $Value = { $InputObject }
      )
    
      begin { $HashTable = @{} }
      process {
        $HashTable.Add(
          ($InputObject | ForEach-Object $Key),
          ($InputObject | ForEach-Object $Value)
        )
      }
      end { $HashTable }
    }
    

    I went for $InputObject | ForEach-Object $Key instead of ForEach-Object $Key -InputObject $InputObject, because I prefer the way it reads, but both should do the job.

    If you're looking for a shorter solution you could also use its alias %, ($InputObject | % $Key), but the linter I'm using doesn't recommend it (AvoidUsingCmdletAliases) so I went with the full cmdlet name.