Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is there any way for a powershell module to get at its caller's scope?

I have a collection of utility functions and other code that I dot-source into every powershell file I write. Having gotten bit by caller scope variables influencing it, I started looking into changing it into a powershell module.

I ran into problems with a couple of the special things I do in it where I actually do want some interaction between the scopes. I'm wondering if there is anyway to "get at" the scope of a module's caller to keep this functionality while moving to a powershell module?

If not, is my best path forward to keep these more specialized things in a dot-sourced file and move the more traditional utility functions into a module? Here are the things that don't easily move to a module:

  • Setting strict mode and error action preferences to keep sane, e.g.:

    Set-StrictMode -Version Latest
    $ErrorActionPreference = "Stop"
    $PSDefaultParameterValues['*:ErrorAction']='Stop'
    

    This (as expected) has no effect on the caller's environment when the code is run from a .psm1 powershell module. Is there any way to cross from the psm1 scope to the caller scope to make these changes?

  • Printing out information about the top-level script call, e.g.:

    $immediateCallerPath = Get-Variable -scope 1 -name PSCommandPath -ValueOnly
    Write-Host "Starting script at $immediateCallerPath"
    $boundParameters = Get-Variable -scope 1 -name PSBoundParameters -ValueOnly
    Write-Host "Bound parameters are:"
    foreach($psbp in $boundParameters.GetEnumerator())
    {
            "({0},{1})" -f $psbp.Key,$psbp.Value | Write-Host
    }
    

    Likewise, these commands can no longer see the top-most calling scope once placed in a .psm1 file

like image 515
aggieNick02 Avatar asked Oct 02 '17 15:10

aggieNick02


People also ask

Are PowerShell variables scoped?

PowerShell scope protects variables and other artifacts by limiting where they can be read and modified. Scope levels protect items that should not be changed. PowerShell has the following scopes available: Global: This scope is available when you open a PowerShell console or create a new runspace or session.

What does $_ mean in PowerShell?

The “$_” is said to be the pipeline variable in PowerShell. The “$_” variable is an alias to PowerShell's automatic variable named “$PSItem“. It has multiple use cases such as filtering an item or referring to any specific object.

Which scope applies to an entire PowerShell instance?

PowerShell supports the following scopes: Global: The scope that is in effect when PowerShell starts or when you create a new session or runspace. Variables and functions that are present when PowerShell starts have been created in the global scope, such as automatic variables and preference variables.

Can PowerShell function access global variable?

Thus, a PowerShell global variable is one that is available throughout the program and can be accessed anywhere. You can set its value in many ways and can create it with many characteristics such as a private global variable, read-only, constant, and more.


1 Answers

$PSCmdlet.SessionState seems to provide a function inside a script module access to the call site's variables provided the call site is outside the module. (If the call site is inside the module, you can just use Get- and Set-Variable -Scope.) Here is an example using SessionState:

New-Module {
    function Get-CallerVariable {
        param([Parameter(Position=1)][string]$Name)
        $PSCmdlet.SessionState.PSVariable.GetValue($Name)
    }
    function Set-CallerVariable {
        param(
            [Parameter(ValueFromPipeline)][string]$Value,
            [Parameter(Position=1)]$Name
        )
        process { $PSCmdlet.SessionState.PSVariable.Set($Name,$Value)}
    }
} | Import-Module

$l = 'original value'
Get-CallerVariable l
'new value' | Set-CallerVariable l
$l

which outputs

original value
new value

I'm not sure whether SessionState was intended to be used in this manner. For what it's worth, this is the same technique used in Get-CallerPreference.ps1. There are also some test cases here which pass on PowerShell versions 2 through 5.1.

like image 151
alx9r Avatar answered Oct 13 '22 00:10

alx9r