Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

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?

like image 446
alx9r Avatar asked Feb 23 '15 01:02

alx9r


People also ask

What is a .psm1 file?

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.

What is the difference between ps1 and psm1?

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.

How do I run a psm1 file in PowerShell?

psm1 files in your $PSModulePath . Then run Import-module module_name . Then the module will be installed and imported.


2 Answers

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
like image 147
user4003407 Avatar answered Oct 19 '22 10:10

user4003407


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.

like image 34
briantist Avatar answered Oct 19 '22 12:10

briantist