【问题标题】:Why does $_ behave differently when in a .ps1 versus .psm1 files?为什么 $_ 在 .ps1 和 .psm1 文件中的行为不同?
【发布时间】:2015-02-23 01:58:41
【问题描述】:

假设您在map.ps1 中定义map_ps

function map_ps{
    [CmdletBinding()]
    param([parameter(ValueFromPipeline=$true)]$InputObject,$sb,$ArgumentList)
    process{&$sb $ArgumentList}
}

假设您还在名为map.psm1well-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 中实现函数时却不是?

【问题讨论】:

    标签: powershell module pipeline


    【解决方案1】:

    除非在全局范围内声明变量,否则函数/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
    

    【讨论】:

    • 谢谢。我刚刚对其进行了测试,即使.psm1 中有两个函数,结果也与您的示例相同。
    • 你能告诉我你是如何学会这种行为的吗?测试?你有一些文件可以告诉我吗?
    • 我从测试中发现的这种行为。当带有模块概念的 PowerShell V2 到来时,我花了一些时间玩它,甚至找到了一些bug。部分知识是通过深入研究 ILSpy 获得的。即使在回答这个问题时,我实际上发现没有模块的 ScriptBlocks 和没有绑定到任何模块的 ScriptBlocks 之间存在差异(通过 [scriptblock]::Create 创建)。
    【解决方案2】:

    我认为这是模块范围和脚本块的组合。位于模块中会改变在脚本块中使用局部变量的方式($_ 在脚本块中用于引用调用者范围内的变量)。

    在脚本块上使用GetNewClosure()

    function map_psm{
        [CmdletBinding()]
        param([parameter(ValueFromPipeline=$true)]$InputObject,$sb,$ArgumentList)
        process{& $sb.GetNewClosure() $ArgumentList}
    }
    

    这应该使用变量的当前值重新评估脚本块。

    【讨论】:

    • 我刚刚测试了这个。这没用。使用GetNewClosure(),输出仍然是DollarBar:, Arg:2
    猜你喜欢
    • 2014-10-25
    • 2017-07-05
    • 2020-09-10
    • 1970-01-01
    • 2022-08-14
    • 1970-01-01
    • 2016-06-02
    • 2016-12-14
    • 2014-06-01
    相关资源
    最近更新 更多