Why does $_ behave differently when in a .ps1 versus .psm1 files? Why does $_ behave differently when in a .ps1 versus .psm1 files? powershell powershell

Why does $_ behave differently when in a .ps1 versus .psm1 files?


Unless variable declared in global scope, functions/ScriptBlocks can not see variables declared in module different from its own module. As workaround, you can create ScriptBlocks thru [scriptblock]::Create, which create ScriptBlocks not bounded to any particular module:

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:SomeValue1|FunctionWithoutModule -sb $ScriptBlockWithModule -ArgumentList 2#DollarBar:, Arg:2, SomeVariable:1|FunctionWithoutModule -sb $ScriptBlockNotBoundToModule -ArgumentList 2#DollarBar:1, Arg:2, SomeVariable:SomeValue1|FunctionWithModule -sb $ScriptBlockWithoutModule -ArgumentList 2#DollarBar:, Arg:2, SomeVariable:1|FunctionWithModule -sb $ScriptBlockWithModule -ArgumentList 2#DollarBar:1, Arg:2, SomeVariable:SomeValue1|FunctionWithModule -sb $ScriptBlockNotBoundToModule -ArgumentList 2#DollarBar:1, Arg:2, SomeVariable:SomeValue


I think this is a combination of Module scope and the scriptblock. Being in a module changes the way that local variables are used within the scriptblock ($_ is being used here inside a scriptblock to refer to a variable in the caller's scope).

Use GetNewClosure() on the Script Block

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

That should re-evaluate the scriptblock using the current value of the variables.