Steve B
Steve B

Reputation: 37690

Settings $_ in a script block invocation

In a complex script, I have a bunch a calls that repeat the same pattern: prepare,execute,clean.

Only the execute part is different, so I want to define only once the prepare and clean calls.

To achieve this, I'd like to wrap this in a function, having the execute part passed as a parameter.

I tried this:

function SomeFunc{
    param(
        [scriptblock]$Action,
        [int]$x,
        [int]$y
    )


    Write-Host "Before"

    Invoke-Command -ScriptBlock $Action  -PipelineVariable ($x*$y) 

    Write-Host "After"

}


SomeFunc  -x 2 -y 4 -Action {  Write-Host -ForegroundColor Yellow "Result is $_"  }

But this not works. $_ is always empty.

How can I reach my goal?

Upvotes: 2

Views: 167

Answers (2)

Varvara Kalinina
Varvara Kalinina

Reputation: 2063

You can pass arguments for the ScriptBlock not through the pipeline but as arguments in array as ArgumentList parameter to the Invoke-Command cmdlet. Then you will be able to access the arguments by $args variable inside your 'process' ScriptBlock.

function SomeFunc {
    param ($x, $y, $sb)
    Write-Host "Before";
    Invoke-Command -ScriptBlock $sb -ArgumentList @("Some string", $x, ($x * $y))
    Write-Host "After";
}
SomeFunc -x 4 -y 2 -sb { foreach ($a in $args) { Write-Host ("Parameter: $a")  } }

The output would be

Before
Parameter: Some string
Parameter: 4
Parameter: 8
After

You can also include param() block inside your ScriptBlock. This way allows you to easily place additional restrictions on the arguments, such as strong typing

function SomeFunc {
    param ($x, $y, $sb)
    Write-Host "Before";
    Invoke-Command -ScriptBlock $sb -ArgumentList @("Not x", $y, ($x * $y));
    Write-Host "After";
}
SomeFunc -x 4 -y 2 -sb { param ([int]$a, $b, $c) Write-Host ("a is {0}, c is {1}, b is {2}" -f $a, $c, $b)} 

The output shows the error

Before
Invoke-Command : Cannot convert value "Not x" to type "System.Int32". Error: "Input string was not in a correct format."
At line:4 char:5
+     Invoke-Command -ScriptBlock $sb -ArgumentList @("Not x", $y, ($x * $y));
+     ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : InvalidArgument: (:) [Invoke-Command], PSInvalidCastException
    + FullyQualifiedErrorId : InvalidCastFromStringToInteger,Microsoft.PowerShell.Commands.InvokeCommandCommand

After

Upvotes: 1

Tony Hinkle
Tony Hinkle

Reputation: 4742

If you don't really need to pipeline to it, you can just use a variable that is set:

function SomeFunc{
    param(
        [scriptblock]$Action,
        [int]$x,
        [int]$y
    )

    $s = $x*$y
    Invoke-Command -ScriptBlock $Action

}

[scriptblock]$sb = { Write-Host -ForegroundColor Yellow "Result is $s" }
SomeFunc  -x 2 -y 4 -Action $sb

Upvotes: 0

Related Questions