如何在调用者的上下文中调用scriptblock?

时间:2017-09-26 14:09:44

标签: function powershell scope pipeline scriptblock

考虑以下呼叫站点:

$modifiedLocal = 'original local value'
'input object' | SomeScriptblockInvoker {
    $modifiedLocal = 'modified local value'
    [pscustomobject] @{
        Local = $local
        DollarBar = $_
    }
}
$modifiedLocal

我想实现SomeScriptblockInvoker这样

  1. 它在模块中定义,
  2. 在调用者的上下文中调用scriptblock。
  3. 呼叫站点的功能输出如下:

    Local DollarBar   
    ----- ---------   
    local input object
    modified local value
    

    PowerShell似乎能够做到这一点。例如,将SomeScriptblockInvoker替换为ForEach-Object会产生所需的输出。

    我接近使用以下定义:

    New-Module m {
        function SomeScriptblockInvoker {
            param
            (
                [Parameter(Position = 1)]
                [scriptblock]
                $Scriptblock,
    
                [Parameter(ValueFromPipeline)]
                $InputObject
            )
            process
            {
                $InputObject | . $Scriptblock
            }
        }
    } |
        Import-Module
    

    使用该定义的呼叫站点的输出如下:

    Local DollarBar
    ----- ---------
    local          
    modified local value
    

    请注意,DollarBar为空时input object

    (gist of Pester tests to check for correct behavior)

1 个答案:

答案 0 :(得分:2)

一般来说,你不能。 scriptblock的调用者无法控制与该scriptblock关联的SessionState,并且SessionState(部分)确定执行scriptblock的上下文(有关详细信息,请参阅Scope部分)。根据脚本块的定义位置,其SessionState可能与调用者的上下文匹配,但可能不匹配。

范围

关于执行scriptblock的上下文,有两个相关的注意事项:

  1. 与scriptblock关联的SessionState。
  2. 调用方法是否将范围添加到SessionState的范围堆栈中。
  3. Here is a good explanation of how this works

    $_自动变量

    $_ contains the current object in the pipeline。提供给%的脚本块的解释与提供.&的脚本块不同:

    • 'input_object' | % {$_} - $_的值为'input_object',因为scriptblock已绑定到%的{​​{1}}参数。该脚本块对管道中的每个对象执行一次。
    • -Process'input_object' | . {process{$_}} - 'input_object' | & {process{$_}}的值为$_,因为scriptblock中的'input_object'位于执行的$_块内一次为管道中的每个对象。

    请注意,在使用process{}调用scriptblock时,OP $_为空。这是因为scriptblock不包含.块。 scriptblock中的每个语句都隐含在scriptblock的process{}块中。运行end{}块时,管道中不再有任何对象,end{}为空。

    $_ vs . vs &

    %.&均使用scriptblock的SessionState调用scriptblock,但根据下表有一些差异:

    %
    • +---+-----------------+-----------+-------------------+----------------------+ | | Name | Kind | interprets {} as | adds scope to stack | +---+-----------------+-----------+-------------------+----------------------+ | % | ForEach-Object | Command | Process block | No | | . | dot-source | Operator | scriptblock | No | | & | call | Operator | scriptblock | Yes | +---+-----------------+-----------+-------------------+----------------------+ 命令包含与%Begin{}块对应的其他参数。
    • 对于scriptblock对与scriptblock关联的SessionState产生副作用的变量赋值,请使用不向堆栈添加范围的调用方法。否则,变量赋值将仅影响新创建的范围,并在scriptblock完成执行后消失。

    最可行的选项

    传递the tests in OP的两个最可行的选项如下。请注意,既不严格在调用者的上下文中调用scriptblock,而是在使用与scriptblock关联的SessionState的上下文中调用。

    选项1

    更改通话网站,以便脚本块包含End{}

    process{}

    使用OP中的$modifiedLocal = 'original local value' 'input object' | SomeScriptblockInvoker { process { $modifiedLocal = 'modified local value' [pscustomobject] @{ Local = $local DollarBar = $_ } } } $modifiedLocal 调用脚本块。

    选项2

    使用SomeScriptblockInvoker作为suggested by PetSerAl调用脚本块。