How to invoke a powershell scriptblock with $_ automatic variable

1k views Asked by At

What works -

Lets say I have a scriptblock which I use with Select-Object cmdlet.

$jobTypeSelector = `
    {
        if ($_.Type -eq "Foo")
        {
            "Bar"
        }
        elseif ($_.Data -match "-Action ([a-zA-Z]+)")
        {
            $_.Type + " [" + $Matches[1] + "]"
        }
        else
        {
            $_.Type
        }
    }

$projectedData = $AllJobs | Select-Object -Property State, @{Name="Type"; Expression=$jobTypeSelector}

This works fine, and I get the results as expected.

What I am trying to do -

However, at a later point in code, I want to reuse the scriptblock defined as $jobTypeSelector.

For example, I expected below code to take $fooJob (note that it is a single object) passed as parameter below, and be used for $_ automatic variable in the scriptblock and return me the same result, as it returns when executed in context of Select-Object cmdlet.

$fooType = $jobTypeSelector.Invoke($fooJob)

What doesn't work -

It does not work as I expected and I get back empty value.

What I have already tried -

  1. I checked, the properties are all correctly set, and it's not due to the relevant property itself being blank or $null.

  2. I looked on the internet, and it's seemed pretty hard to find any relevant page on the subject, but I eventually found one which was close to explaining the issue in a slightly different context - calling the script blocks in PowerShell. The blog doesn't directly answer my question, and any relevant explanation only leads to a solution which would be very ugly, hard to read and maintain in my opinion.

Question -

So, what is the best way to invoke a scriptblock for a single object, which uses $_ automatic variable as parameter, (instead of param block)

2

There are 2 answers

0
Vikas Gupta On BEST ANSWER

After fiddling around with varoius options, I ended up sloving the problem, in a sort of Hackish way.. But I find it to be the best solution because it's small, easy to read, maintain and understand.

Even though we are talking about single object, use it in the pipeline (which is when PowerShell defines the $_ automatic variable) with ForEach-Object cmdlet

$fooType = $fooJob | ForEach-Object $jobTypeSelector
1
Sam Porch On

You can certainly use foreach or ForEach-Object as you mention.

You can also pipe to the ScriptBlock directly, if you change it from a function ScriptBlock to a filter ScriptBlock by setting IsFilter to $true:

$jobTypeSelector.IsFilter = $true

$fooType = $fooJob | $jobTypeSelector

But, what would be even better is if you used a named function instead of an anonymous ScriptBlock, for example:

function Get-JobType
{
    Param ( 
        [object] $Job 
    )

    if ($Job.Type -eq "Foo")
    {
        "Bar"
    }
    elseif ($Job.Data -match "-Action ([a-zA-Z]+)")
    {
        $Job.Type + " [" + $Matches[1] + "]"
    }
    else
    {
        $Job.Type
    }
}

Then you can use it with Select-Object aka select like this:

$projectedData = $AllJobs |
    select -Property State, @{Name="Type"; Expression={Get-JobType $_}}

Or with a single job, like this:

$fooType = Get-JobType $fooJob