有没有办法让 powershell 模块进入其调用者的范围?

agg*_*k02 7 powershell powershell-module

我有一组实用函数和其他代码,我将它们点源到我编写的每个 powershell 文件中。在被调用者作用域变量影响后,我开始考虑将其更改为 powershell 模块。

我在其中做的一些特殊事情遇到了问题,实际上我确实希望在范围之间进行一些交互。我想知道是否有任何方式“进入”模块调用者的范围以在移动到 powershell 模块时保持此功能?

如果没有,我将这些更专业的东西保存在一个点源文件中并将更传统的实用程序功能移动到一个模块中是我最好的方法吗?以下是不容易移至模块的内容:

  • 设置严格模式和错误操作首选项以保持理智,例如:

    Set-StrictMode -Version Latest
    $ErrorActionPreference = "Stop"
    $PSDefaultParameterValues['*:ErrorAction']='Stop'
    
    Run Code Online (Sandbox Code Playgroud)

    当代码从 .psm1 powershell 模块运行时,这(如预期)对调用者的环境没有影响。有没有办法从 psm1 范围跨越到调用者范围来进行这些更改?

  • 打印出有关顶级脚本调用的信息,例如:

    $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
    }
    
    Run Code Online (Sandbox Code Playgroud)

    同样,这些命令一旦放置在 .psm1 文件中就无法再看到最顶层的调用范围

alx*_*x9r 6

$PSCmdlet.SessionState如果调用站点在模块外部,似乎在脚本模块内提供了一个函数来访问调用站点的变量。(如果调用站点在模块内,则可以使用Get-Set-Variable -Scope。)这是一个使用 的示例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
Run Code Online (Sandbox Code Playgroud)

哪个输出

original value
new value
Run Code Online (Sandbox Code Playgroud)

我不确定是否SessionState打算以这种方式使用。就其价值而言,这与Get-CallerPreference.ps1. 也有一些测试案例在这里它通过对PowerShell的版本2至5.1。


Ale*_*ian 0

不确定我是否完全理解你的目的。我相信您想知道调用模块的 cmdlet 的代码在哪里实现。也许更进一步。

如果我是正确的,那么您可以用来Get-PSCallStack检索堆栈跟踪。例如,从未保存的脚本来看,它看起来像这样

PS C:\Users\asarafian> Get-PSCallStack

Command       Arguments Location 
-------       --------- -------- 
<ScriptBlock> {}        <No file>
Run Code Online (Sandbox Code Playgroud)

如果文件已保存,那么它看起来像这样

PS C:\Users\asarafian> Get-PSCallStack

Command       Arguments Location 
-------       --------- -------- 
File1.ps1           <No file>
Run Code Online (Sandbox Code Playgroud)

根据您想要实现的目标(我不清楚),您需要遍历执行到[0]的代码的列表。Get-PSCallStack[x]

在构建XWrite时,我还想弄清楚堆栈中的条目是否是脚本文件、模块的 cmdlet 部分或未知的<ScriptBlock>.

我的实现位于Get-XCommandSource.ps1中,它遵循堆栈跟踪中的命令值的以下逻辑

  1. 如果以结尾.ps1则它是一个脚本文件。
  2. 如果是,<ScriptBlock>那么它是一个脚本块。
  3. 如果该命令可以加载Get-Command然后
    1. 如果它有模块,那么它就是模块中的 cmdlet。
    2. 如果不是,那么它是使用模式导入的 cmdlet/函数.\cmdlet.ps1

这是实现:

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 {

    }
}
Run Code Online (Sandbox Code Playgroud)