提问人:Andrea Bardelli 提问时间:7/26/2023 最后编辑:Andrea Bardelli 更新时间:7/26/2023 访问量:54
访问 PowerShell 中的父 scriptblock 作用域
access parent scriptblock scope in powershell
问:
作为学习其基础知识的一部分,我正在 pws 中实现三元运算符 cmdlet。 我让它采用脚本块来模拟条件评估三元运算符通常具有的条件评估。 在大多数情况下,它工作正常。
function valOf($var){
if($var -is [scriptblock]){
return & $var }
else {
return $var }
}
function ternary([bool]$condition, $t, $f){
if($condition){
return valOf($t) }
else{
return valOf($f) }
#return @($t,$f)[!$condition]
}
当我开始嵌套脚本块时,我遇到了麻烦:
$i=56;
&{
$i=0
ternary($true) {$script:i+=2} {write-host "onFalse"}
$i #wanted:2 #reality: 58
<# without '$script: $i' is indeed 0, but cannot be edited #>
}
$i #wanted:56 #reality:58
如何访问中间范围?
浏览文档和论坛 这似乎是一个很常见的问题,但主题并不明确 x.x
也许是一个从 copyOnWrite 行为中选择退出的 invokeCommand..?
答:
2赞
Santiago Squarzon
7/26/2023
#1
假设您的函数是高级函数,例如(注意装饰):ternary
[cmdletbinding()]
function ternary {
[CmdletBinding()]
param(
[bool] $condition,
[scriptblock] $ifTrue,
[scriptblock] $ifFalse
)
if ($condition) {
return & $ifTrue
}
& $ifFalse
}
然后,可以利用$PSCmdlet
获取和更新内部作用域中的值:$i
$i = 56
& {
$i = 0
ternary $true { $PSCmdlet.SessionState.PSVariable.Get('i').Value += 2 } { Write-Host 'onFalse' }
$i # 2
}
$i # 56
如果 不高级,则可以将 Get-Variable
定位范围用于此答案中的函数:ternary
2
ternary $true { (Get-Variable i -Scope 2).Value += 2 } { Write-Host 'onFalse' }
对于问题中的函数,您需要使用作用域,因为脚本块是通过添加到作用域来传递和执行的。3
valOf
+1
就个人而言,我会将您的功能更改为:
function ternary([bool] $condition, $t, $f) {
if ($condition) {
if ($t -is [scriptblock]) {
return & $t
}
return $t
}
if ($f -is [scriptblock]) {
return & $f
}
$f
}
然后,您可以毫无问题地使用。-Scope 2
在这两种情况下,无论是高级还是非高级,使用$ExecutionContext
也应该有效:
ternary $true { $ExecutionContext.SessionState.PSVariable.Get('i').Value += 2 } { Write-Host 'onFalse' }
评论
0赞
Andrea Bardelli
7/26/2023
我尝试使用 -scope 0、1 和 2,它们都给了我错误“找不到名称为'i'的变量”
1赞
Santiago Squarzon
7/26/2023
@AndreaBardelli呵呵,用你的函数,那么作用域是,因为正在传递对象,然后执行脚本块,将 +1 添加到作用域。相反,你应该做的是检查脚本块本身是否和是脚本块,或者至少在其中执行它们3
ternary
valOf
$f
$t
ternary
0赞
Santiago Squarzon
7/26/2023
@AndreaBardelli我更新以澄清这一点。希望为什么需要您的函数范围是有道理的3
0赞
Andrea Bardelli
7/26/2023
首先是答案。最重要的是不必使用冗长的语法,因为三元运算符的主要目的是简洁。但对于所有其他目的来说,这很好
2赞
mklement0
7/26/2023
#2
Santiago 的有用答案的替代方案,即无需在传递给三元函数的脚本块参数中使用特殊语法:
注意:为简洁起见:
下面的函数不处理参数不是脚本块的情况,但这很容易添加。
ternary
省略该属性;严格来说,你并不需要一个高级函数来使解决方案工作,尽管这当然是可取的,并且添加类似的东西会隐式地使你的函数成为一个高级函数。
[CmdletBinding()]
[Parameter(Mandatory)]
# Create (and implicitly import) a dynamic module that
# hosts the ternary function.
$null = New-Module {
# Define the ternary function.
function ternary {
param(
[bool] $Condition,
[scriptblock] $trueBlock,
[scriptblock] $falseBlock
)
# Dot-source the appropriate script block,
# which runs directly in the *caller's* scope,
# given that's where it was created and given that the
# module's code runs in a separate scope domain ("session state")
if ($Condition) { . $trueBlock } else { . $falseBlock }
}
}
$i=56;
& {
$i=0
# Now you can use $i as-is
# in order to refer to the current scope's $i.
ternary $true { $i+=2 } {write-host "onFalse"}
$i # -> 2
}
$i # -> 56
注意:
评论
0赞
Andrea Bardelli
7/26/2023
这看起来就像我一直在寻找的一样^^它也可以与普通的 .psm1 模块一起使用吗?
0赞
mklement0
7/26/2023
@AndreaBardelli,是的,它应该;请参阅我的更新,其中还提供了有关 PowerShell 中(模块)范围的详细信息的链接。
评论
$i
Get-Variable i -Scope 0
Get-Variable i -Scope 1