考虑以下 call 站点:

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

我想实现SomeScriptblockInvoker这样
  • 它是在模块中定义的,
  • 在调用者的上下文中调用脚本块。

  • 调用站点上的函数输出如下:
    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
    

    使用该定义的 call 站点的输出如下:
    Local DollarBar
    ----- ---------
    local
    modified local value
    

    请注意,DollarBar应该为input object时为空。

    (gist of Pester tests to check for correct behavior)

    最佳答案

    通常,您不能。脚本块的调用者无法控制与该脚本块相关联的SessionState,并且SessionState确定(部分)确定执行脚本块的上下文(有关详细信息,请参见“作用域”部分)。根据脚本块的定义位置,其SessionState可能与调用者的上下文匹配,但可能不匹配。

    范围

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

  • 与脚本块关联的SessionState。
  • 调用方法是否将范围添加到SessionState的范围堆栈。

  • Here is a good explanation of how this works
    $_自动变量

    $_ contains the current object in the pipeline。提供给%的脚本块的解释与提供的.&的脚本块不同:
  • 'input_object' | % {$_}-$_的值为'input_object',因为脚本块绑定(bind)到%-Process参数。该脚本块对管道中的每个对象执行一次。
  • 'input_object' | . {process{$_}}'input_object' | & {process{$_}}-$_的值为'input_object',因为脚本块中的$_位于process{}块内,该块对管道中的每个对象执行一次。

  • 请注意,在OP中,使用$_调用脚本块时,.为空。这是因为脚本块不包含process{}块。脚本块中的每个语句都隐式属于脚本块end{}块的一部分。到运行end{}块时,管道中不再有任何对象,并且$_为null。
    . vs & vs %.&%各自使用脚本块的SessionState调用脚本块,但根据下表有所不同:
    +---+-----------------+-----------+-------------------+----------------------+
    |   |      Name       |    Kind   |  interprets {} as |  adds scope to stack |
    +---+-----------------+-----------+-------------------+----------------------+
    | % |  ForEach-Object |  Command  |  Process block    |  No                  |
    | . |  dot-source     |  Operator |  scriptblock      |  No                  |
    | & |  call           |  Operator |  scriptblock      |  Yes                 |
    +---+-----------------+-----------+-------------------+----------------------+
    
  • %命令具有与Begin{}End{}块相对应的其他参数。
  • 要使脚本块进行的变量分配对与脚本块关联的SessionState产生副作用,请使用不向堆栈添加范围的调用方法。否则,变量分配将仅影响新创建的范围,并在脚本块完成执行后消失。

  • 最可行的选择

    传递the tests in OP的两个最可行的方法如下。请注意,两者均未严格在调用者的上下文中调用脚本块,而是在使用与脚本块相关联的SessionState的上下文中调用脚本块。

    选项1

    更改 call 站点,以便脚本块包含process{}:
    $modifiedLocal = 'original local value'
    'input object' | SomeScriptblockInvoker {
        process {
            $modifiedLocal = 'modified local value'
            [pscustomobject] @{
                Local = $local
                DollarBar = $_
            }
        }
    }
    $modifiedLocal
    

    并在OP中使用SomeScriptblockInvoker调用脚本块。

    选项2

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

    关于function - 如何在调用者的上下文中调用脚本块?,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/46428736/

    10-11 08:34