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 s
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-PSCallStack
Command Arguments Location
------- --------- --------
<ScriptBlock> {} <No file>
If the file was saved then it would look like this
PS C:\Users\asarafian> Get-PSCallStack
Command 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
.ps1
then it's a script file.<ScriptBlock>
then it's a script block.Get-Command
then
.\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 {
}
}
$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.