如何在调用者的上下文中调用脚本块?
How can I invoke a scriptblock in the caller's context?
考虑以下调用站点:
$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
使用该定义的调用站点的输出如下:
Local DollarBar
----- ---------
local
modified local value
请注意 DollarBar
是空的,而它应该是 input object
。
一般来说,你不能。脚本块的调用者无法控制与该脚本块关联的 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'
因为脚本块绑定到 %
的 -Process
参数。该脚本块对管道中的每个对象执行一次。
'input_object' | . {process{$_}}
和 'input_object' | & {process{$_}}
- $_
的值为 'input_object'
因为脚本块中的 $_
在 process{}
块内对管道中的每个对象执行一次。
请注意,在使用 .
调用脚本块时,OP $_
为空。这是因为脚本块不包含 process{}
块。脚本块中的每个语句都是脚本块的 end{}
块的隐含部分。当 end{}
块为 运行 时,管道中不再有任何对象并且 $_
为空。
.
对比 &
对比 %
.
、&
和 %
均使用脚本块的 SessionState 调用脚本块,但根据以下 table:
+---+-----------------+-----------+-------------------+----------------------+
| | 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
更改调用站点,使脚本块包含 process{}
:
$modifiedLocal = 'original local value'
'input object' | SomeScriptblockInvoker {
process {
$modifiedLocal = 'modified local value'
[pscustomobject] @{
Local = $local
DollarBar = $_
}
}
}
$modifiedLocal
并在 OP 中使用 SomeScriptblockInvoker
调用脚本块。
选项 2
使用 %
作为 调用脚本块。
考虑以下调用站点:
$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
使用该定义的调用站点的输出如下:
Local DollarBar
----- ---------
local
modified local value
请注意 DollarBar
是空的,而它应该是 input object
。
一般来说,你不能。脚本块的调用者无法控制与该脚本块关联的 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'
因为脚本块绑定到%
的-Process
参数。该脚本块对管道中的每个对象执行一次。'input_object' | . {process{$_}}
和'input_object' | & {process{$_}}
-$_
的值为'input_object'
因为脚本块中的$_
在process{}
块内对管道中的每个对象执行一次。
请注意,在使用 .
调用脚本块时,OP $_
为空。这是因为脚本块不包含 process{}
块。脚本块中的每个语句都是脚本块的 end{}
块的隐含部分。当 end{}
块为 运行 时,管道中不再有任何对象并且 $_
为空。
.
对比 &
对比 %
.
、&
和 %
均使用脚本块的 SessionState 调用脚本块,但根据以下 table:
+---+-----------------+-----------+-------------------+----------------------+
| | 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
更改调用站点,使脚本块包含 process{}
:
$modifiedLocal = 'original local value'
'input object' | SomeScriptblockInvoker {
process {
$modifiedLocal = 'modified local value'
[pscustomobject] @{
Local = $local
DollarBar = $_
}
}
}
$modifiedLocal
并在 OP 中使用 SomeScriptblockInvoker
调用脚本块。
选项 2
使用 %
作为