function - 如何在调用者的上下文中调用脚本 block ?

标签 function powershell scope pipeline scriptblock

考虑以下调用站点:

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

我想实现 SomeScriptblockInvoker 这样

  1. 它是在模块中定义的,并且
  2. 脚本 block 在调用者的上下文中被调用。

调用站点的函数输出如下:

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 应该是输入对象,但它是空的。

(gist of Pester tests to check for correct behavior)

最佳答案

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

范围

关于执行脚本 block 的上下文,有两个相关的考虑因素:

  1. 与脚本 block 关联的 SessionState。
  2. 调用方法是否将作用域添加到 SessionState 的作用域堆栈中。

Here is a good explanation of how this works .

$_ 自动变量

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

  • '输入对象' | % {$_} - $_ 的值为 'input_object',因为脚本 block 绑定(bind)到 %-Process 参数。该脚本 block 针对管道中的每个对象执行一次。
  • '输入对象' | 。 {process{$_}}'input_object' | & {process{$_}} - $_ 的值为 'input_object',因为脚本 block 中的 $_位于 process{} block 内,该 block 对管道中的每个对象执行一次。

请注意,当使用 . 调用脚本 block 时,OP $_ 为空。这是因为脚本 block 不包含 process{} block 。脚本 block 中的每个语句都是脚本 block 的 end{} block 的隐式部分。当 end{} block 运行时,管道中不再有任何对象,并且 $_ 为 null。

.&%

.&% 均使用脚本 block 的 SessionState 调用脚本 block ,但根据下表存在一些差异:

+---+-----------------+-----------+-------------------+----------------------+
|   |      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{} block 相对应的其他参数。
  • 为了使脚本 block 进行的变量分配对与脚本 block 关联的 SessionState 产生副作用,请使用不向堆栈添加作用域的调用方法。否则,变量赋值只会影响新创建的作用域,并在脚本 block 执行完成后消失。

最可行的选择

通过the tests in OP的两个最可行的选择如下面所述。请注意,两者都不是严格在调用者的上下文中调用脚本 block ,而是在使用与脚本 block 关联的 SessionState 的上下文中调用脚本 block 。

选项 1

更改调用站点,以便脚本 block 包含process{}:

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

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

选项 2

使用 % 作为 suggested by PetSerAl 调用脚本 block .

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

相关文章:

c - 函数入门需要帮助输入数字(初学者)

javascript - 如何将值从函数传递到 SVG 标签?

来自标准信号库的 C 声明

ruby - 有没有办法在 RSpec 中获取当前范围级别?

function - std::function 可以接受仿函数吗?

arrays - 我如何有一个从 args 或 Powershell 管道中获取输入的数组参数?

powershell - 读取文本文件的第一行,然后将以下行传递给循环读取

powershell - 如何关闭由 Invoke-WebRequest 打开的 IE session

javascript - 在 Javascript 中存储来自 AWS S3 SDK 的 listObject 数据

python - 变量作用域(Python 新手)