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
?
As the name implies, a script module is a file ( . psm1 ) that contains any valid Windows PowerShell code. Script developers and administrators can use this type of module to create modules whose members include functions, variables, and more.
While the only difference between the two is the extension, it is far more cumbersome to develop directly with the psm1 files as they cannot be directly executed. A simple and convenient workaround is to develop with ps1 files but convert them to psm1 during the build process.
psm1 files in your $PSModulePath . Then run Import-module module_name . Then the module will be installed and imported.
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
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With