看起来和GetNewClosure()
任何解决方法一样好,但它改变了脚本块查看这些变量的方式。$_
作为参数传递给脚本块也可以。
它与正常范围问题(例如,全局与本地)无关,但起初看起来像这样。这是我非常简化的复制和以下一些解释:
script.ps1
对于正常的点源:
function test-script([scriptblock]$myscript){
$message = "inside"
&{write-host "`$message from $message"}
&$myscript
}
Module\MyTest\MyTest.psm1
用于导入:
function test-module([scriptblock]$myscript){
$message = "inside"
&{write-host "`$message from $message"}
&$myscript
}
function test-module-with-closure([scriptblock]$myscript){
$message = "inside"
&{write-host "`$message from $message"}
&$myscript.getnewclosure()
}
调用和输出:
» . .\script.ps1
» import-module mytest
» $message = "outside"
» $block = {write-host "`$message from $message (inside?)"}
» test-script $block
$message from inside
$message from inside (inside?)
» test-module $block
$message from inside
$message from outside (inside?)
» test-module-with-closure $block
$message from inside
$message from inside (inside?)
所以我开始四处寻找,因为这激起了我的好奇心,我发现了一些有趣的东西。
这个 Q&A还包含指向此错误报告的链接,与我遇到的其他一些博客文章的主题几乎完全相同。但是,虽然它被报告为错误,但我不同意。
about_Scopes页面有这样的说法(w:
...
Restricting Without Scope
A few Windows PowerShell concepts are similar to scope or interact with
scope. These concepts may be confused with scope or the behavior of scope.
Sessions, modules, and nested prompts are self-contained environments,
but they are not child scopes of the global scope in the session.
...
Modules:
...
The privacy of a module behaves like a scope, but adding a module
to a session does not change the scope. And, the module does not have
its own scope, although the scripts in the module, like all Windows
PowerShell scripts, do have their own scope.
现在我理解了这种行为,但正是上述情况和其他一些实验让我明白了这一点:
- 如果我们将脚本块更改
$message
为,$local:message
那么所有 3 个测试都有一个空格,因为$message
未在脚本块的本地范围中定义。
- 如果我们使用
$global:message
,所有 3 个测试都会打印outside
。
- 如果我们使用
$script:message
,前 2 个测试打印outside
,最后一个打印inside
。
然后我也读到了这个about_Scopes
:
Numbered Scopes:
You can refer to scopes by name or by a number that
describes the relative position of one scope to another.
Scope 0 represents the current, or local, scope. Scope 1
indicates the immediate parent scope. Scope 2 indicates the
parent of the parent scope, and so on. Numbered scopes
are useful if you have created many recursive
scopes.
- 如果我们使用
$((get-variable -name message -scope 1).value)
为了尝试从直接父作用域获取值,会发生什么?我们仍然得到outside
而不是inside
。
在这一点上,我很清楚会话和模块有自己的声明范围或某种上下文,至少对于脚本块来说是这样。脚本块在声明它们的环境中就像匿名函数一样,直到您调用GetNewClosure()
它们,此时它们将它们在被调用的范围内引用的同名变量的副本内部化GetNewClosure()
(首先使用局部变量,最多全局变量)。快速演示:
$message = 'first message'
$sb = {write-host $message}
&$sb
#output: first message
$message = 'second message'
&$sb
#output: second message
$sb = $sb.getnewclosure()
$message = 'third message'
&$sb
#output: second message
我希望这有帮助。
附录:关于设计。
JasonMArcher 的评论让我想到了将脚本块传递到模块中的设计问题。在您问题的代码中,即使您使用GetNewClosure()
解决方法,您也必须知道脚本块将在其中执行的变量的名称才能使其工作。
另一方面,如果你使用参数给脚本块并$_
作为参数传递给它,脚本块不需要知道变量名,它只需要知道将传递一个特定类型的参数。因此,您的模块将使用$props = & $Properties $_
而不是$props = & $Properties.GetNewClosure()
,并且您的脚本块看起来更像这样:
{ (param [System.IO.FileInfo]$fileinfo)
Write-Host Creating properties for $fileinfo.FullName
@{Name=$fileinfo.Name } # any other properties based on the file
}
有关进一步说明,请参阅 CosmosKey 的答案。