为什么 $_ 在 .ps1 和 .psm1 文件中表现不同?
Why does $_ behave differently when in a .ps1 versus .psm1 files?
假设您在 map.ps1
中定义 map_ps
:
function map_ps{
[CmdletBinding()]
param([parameter(ValueFromPipeline=$true)]$InputObject,$sb,$ArgumentList)
process{&$sb $ArgumentList}
}
假设您还在名为 map.psm1
:
的 well-formed 模块中定义了另一个具有 相同实现 的函数 map_psm
function map_psm{
[CmdletBinding()]
param([parameter(ValueFromPipeline=$true)]$InputObject,$sb,$ArgumentList)
process{&$sb $ArgumentList}
}
使用相同参数调用每个函数不会产生相同的结果:
PS C:\> 1 | map_ps -sb {"DollarBar:$_, Arg:$($args[0])"} -ArgumentList 2
DollarBar:1, Arg:2
PS C:\> 1 | map_psm -sb {"DollarBar:$_, Arg:$($args[0])"} -ArgumentList 2
DollarBar:, Arg:2
为什么在 .psm1
中实现函数时 $_
为空,而在 .ps1
中实现函数时却不是?
我认为这是模块作用域和脚本块的组合。在模块中会改变局部变量在脚本块中的使用方式($_
在脚本块中用于引用调用者作用域中的变量)。
在脚本块上使用 GetNewClosure()
function map_psm{
[CmdletBinding()]
param([parameter(ValueFromPipeline=$true)]$InputObject,$sb,$ArgumentList)
process{& $sb.GetNewClosure() $ArgumentList}
}
应该使用变量的当前值重新评估脚本块。
除非在全局范围内声明了变量,functions/ScriptBlocks 看不到在与其自身模块不同的模块中声明的变量。作为解决方法,您可以通过 [scriptblock]::Create
创建 ScriptBlocks,这会创建不绑定到任何特定模块的 ScriptBlocks:
function FunctionWithoutModule{
[CmdletBinding()]
param([parameter(ValueFromPipeline=$true)]$InputObject,$sb,$ArgumentList)
process{
$SomeVariable='SomeValue'
&$sb $ArgumentList
}
}
$Module=New-Module -ScriptBlock {
function FunctionWithModule{
[CmdletBinding()]
param([parameter(ValueFromPipeline=$true)]$InputObject,$sb,$ArgumentList)
process{
$SomeVariable='SomeValue'
&$sb $ArgumentList
}
}
}
$ScriptBlockWithoutModule={"DollarBar:$_, Arg:$($args[0]), SomeVariable:$SomeVariable"}
$ScriptBlockWithModule=$Module.NewBoundScriptBlock($ScriptBlockWithoutModule)
$ScriptBlockNotBoundToModule=[scriptblock]::Create($ScriptBlockWithoutModule)
1|FunctionWithoutModule -sb $ScriptBlockWithoutModule -ArgumentList 2
#DollarBar:1, Arg:2, SomeVariable:SomeValue
1|FunctionWithoutModule -sb $ScriptBlockWithModule -ArgumentList 2
#DollarBar:, Arg:2, SomeVariable:
1|FunctionWithoutModule -sb $ScriptBlockNotBoundToModule -ArgumentList 2
#DollarBar:1, Arg:2, SomeVariable:SomeValue
1|FunctionWithModule -sb $ScriptBlockWithoutModule -ArgumentList 2
#DollarBar:, Arg:2, SomeVariable:
1|FunctionWithModule -sb $ScriptBlockWithModule -ArgumentList 2
#DollarBar:1, Arg:2, SomeVariable:SomeValue
1|FunctionWithModule -sb $ScriptBlockNotBoundToModule -ArgumentList 2
#DollarBar:1, Arg:2, SomeVariable:SomeValue
假设您在 map.ps1
中定义 map_ps
:
function map_ps{
[CmdletBinding()]
param([parameter(ValueFromPipeline=$true)]$InputObject,$sb,$ArgumentList)
process{&$sb $ArgumentList}
}
假设您还在名为 map.psm1
:
map_psm
function map_psm{
[CmdletBinding()]
param([parameter(ValueFromPipeline=$true)]$InputObject,$sb,$ArgumentList)
process{&$sb $ArgumentList}
}
使用相同参数调用每个函数不会产生相同的结果:
PS C:\> 1 | map_ps -sb {"DollarBar:$_, Arg:$($args[0])"} -ArgumentList 2
DollarBar:1, Arg:2
PS C:\> 1 | map_psm -sb {"DollarBar:$_, Arg:$($args[0])"} -ArgumentList 2
DollarBar:, Arg:2
为什么在 .psm1
中实现函数时 $_
为空,而在 .ps1
中实现函数时却不是?
我认为这是模块作用域和脚本块的组合。在模块中会改变局部变量在脚本块中的使用方式($_
在脚本块中用于引用调用者作用域中的变量)。
在脚本块上使用 GetNewClosure()
function map_psm{
[CmdletBinding()]
param([parameter(ValueFromPipeline=$true)]$InputObject,$sb,$ArgumentList)
process{& $sb.GetNewClosure() $ArgumentList}
}
应该使用变量的当前值重新评估脚本块。
除非在全局范围内声明了变量,functions/ScriptBlocks 看不到在与其自身模块不同的模块中声明的变量。作为解决方法,您可以通过 [scriptblock]::Create
创建 ScriptBlocks,这会创建不绑定到任何特定模块的 ScriptBlocks:
function FunctionWithoutModule{
[CmdletBinding()]
param([parameter(ValueFromPipeline=$true)]$InputObject,$sb,$ArgumentList)
process{
$SomeVariable='SomeValue'
&$sb $ArgumentList
}
}
$Module=New-Module -ScriptBlock {
function FunctionWithModule{
[CmdletBinding()]
param([parameter(ValueFromPipeline=$true)]$InputObject,$sb,$ArgumentList)
process{
$SomeVariable='SomeValue'
&$sb $ArgumentList
}
}
}
$ScriptBlockWithoutModule={"DollarBar:$_, Arg:$($args[0]), SomeVariable:$SomeVariable"}
$ScriptBlockWithModule=$Module.NewBoundScriptBlock($ScriptBlockWithoutModule)
$ScriptBlockNotBoundToModule=[scriptblock]::Create($ScriptBlockWithoutModule)
1|FunctionWithoutModule -sb $ScriptBlockWithoutModule -ArgumentList 2
#DollarBar:1, Arg:2, SomeVariable:SomeValue
1|FunctionWithoutModule -sb $ScriptBlockWithModule -ArgumentList 2
#DollarBar:, Arg:2, SomeVariable:
1|FunctionWithoutModule -sb $ScriptBlockNotBoundToModule -ArgumentList 2
#DollarBar:1, Arg:2, SomeVariable:SomeValue
1|FunctionWithModule -sb $ScriptBlockWithoutModule -ArgumentList 2
#DollarBar:, Arg:2, SomeVariable:
1|FunctionWithModule -sb $ScriptBlockWithModule -ArgumentList 2
#DollarBar:1, Arg:2, SomeVariable:SomeValue
1|FunctionWithModule -sb $ScriptBlockNotBoundToModule -ArgumentList 2
#DollarBar:1, Arg:2, SomeVariable:SomeValue