Reputation: 4241
Suppose you define map_ps
in map.ps1
:
function map_ps{
[CmdletBinding()]
param([parameter(ValueFromPipeline=$true)]$InputObject,$sb,$ArgumentList)
process{&$sb $ArgumentList}
}
Suppose you also define another function map_psm
with identical implementation in a well-formed module called map.psm1
:
function map_psm{
[CmdletBinding()]
param([parameter(ValueFromPipeline=$true)]$InputObject,$sb,$ArgumentList)
process{&$sb $ArgumentList}
}
Calling each function with identical parameters does not yield the same result:
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
Why is $_
empty when the function is implement in a .psm1
but it isn't when the function is implemented in a .ps1
?
Upvotes: 6
Views: 331
Reputation: 22122
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: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
Upvotes: 5
Reputation: 47802
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).
GetNewClosure()
on the Script Blockfunction 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.
Upvotes: 2