Is there any way for a powershell module to get at its caller's scope?
$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 valuenew 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.
Not sure if I understand completely what you are after. I believe you would like to know where is the code implemented that invokes the cmdlets of you module. Maybe even further up.
If I'm correct then you can use Get-PSCallStack
to retrieve the stack trace. For example from an unsaved script it looks like this
PS C:\Users\asarafian> Get-PSCallStackCommand Arguments Location ------- --------- -------- <ScriptBlock> {} <No file>
If the file was saved then it would look like this
PS C:\Users\asarafian> Get-PSCallStackCommand Arguments Location ------- --------- -------- File1.ps1 <No file>
Depending on what you want to achieve, which is not clear to me, you need to walk the list from [0]
which is the code that executed Get-PSCallStack
to [x]
.
When building the XWrite I wanted also to figure out if an entry from the stack was a script file, a cmdlet part of a module or unknown like <ScriptBlock>
.
My implementation is in Get-XCommandSource.ps1 and it follows the following logic for the command value from the stacktrace
- if it ends with
.ps1
then it's a script file. - if it is
<ScriptBlock>
then it's a script block. - if the command can be loaded with
Get-Command
then- if it has a module then it's a cmdlet in a module.
- if not then it's a cmdlet/function imported with
.\cmdlet.ps1
pattern.
Here is the implementation:
function Get-XCommandSource{ [CmdletBinding(SupportsShouldProcess=$true)] Param( [Parameter(Mandatory=$true)] [AllowEmptyString()] [AllowNull()] [string]$Command ) begin { } process { if(-not $Command) { "Unknown" } elseif($Command.EndsWith(".ps1")) { "Script" } elseif($Command -eq "<scriptblock>") { "Unknown" } else { $cmdlet=Get-Command -Name $command -ErrorAction SilentlyContinue if($cmdlet) { $moduleName=$cmdlet|Select-Object -ExpandProperty ModuleName if($moduleName) { $moduleName } else { "Function" } } else { "Unknown" } } } end { }}