7

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

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

如果不是,那么将这些更专业的东西保存在点源文件中并将更传统的实用程序功能移动到模块中是我最好的前进路径吗?以下是不容易移动到模块的内容:

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

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

    当代码从 .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
    }
    

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

4

2 回答 2

6

$PSCmdlet.SessionState似乎在脚本模块内部提供了一个函数来访问调用站点的变量,前提是调用站点位于模块外部。(如果调用站点在模块内,您可以只使用Get-and 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

哪个输出

original value
new value

我不确定是否SessionState打算以这种方式使用。值得一提的是,这与Get-CallerPreference.ps1. 这里还有一些测试用例通过 PowerShell 版本 2 到 5.1。

于 2017-10-12T21:11:36.513 回答
0

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

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

PS C:\Users\asarafian> Get-PSCallStack

Command       Arguments Location 
-------       --------- -------- 
<ScriptBlock> {}        <No file>

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

PS C:\Users\asarafian> Get-PSCallStack

Command       Arguments Location 
-------       --------- -------- 
File1.ps1           <No file>

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

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

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

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

这是实现:

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 {

    }
}
于 2017-10-03T08:20:34.120 回答