You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在PowerShell全局作用域创建带原脚本错误路径的函数?

问题

假设有一个名为s.ps1的PowerShell脚本,内容如下:

1/0

我们需要实现一个New-GlobalFunction函数,它的定义如下:

NAME
    New-GlobalFunction

SYNOPSIS
    Creates a new function in global scope from a script.

SYNTAX
    New-GlobalFunction [-ScriptPath] <string> [-FunctionName] <string>

目标是通过New-GlobalFunction定义一个全局函数Invoke-S,调用Invoke-S时产生的错误位置信息要能显示原文件s.ps1,示例输出如下:

PS C:\> New-GlobalFunction -ScriptPath .\s.ps1 -FunctionName Invoke-S; Invoke-S
RuntimeException: C:\s.ps1:1
Line |
   1 |  1/0
     |  ~~~
     | Attempted to divide by zero.
----------

Attempted to divide by zero.
at <ScriptBlock>, C:\s.ps1: line 1

注意错误信息中明确指向了原文件s.ps1。

PowerShell的架构支持这种需求,因为脚本和函数本质上都是脚本块:

在PowerShell编程语言中,脚本块是可作为单个单元使用的语句或表达式集合。这些语句集合可以用大括号({})括起来、定义为函数,或者保存到脚本文件中。

实际上,s.ps1对应的脚本块可以通过Get-Command .\s.ps1 | % ScriptBlock获取,直接调用这个脚本块就能得到包含正确位置信息的错误。甚至可以通过以下命令将其转换为本地函数:

New-Item -Path Function: -Name Invoke-S -Value (Get-Command .\s.ps1 | % ScriptBlock)

但目前无法成功创建具备同样错误位置信息的全局函数。

请问是否存在一种方式实现New-GlobalFunction,使得由-ScriptPath指定文件中的脚本块产生的错误能在位置信息中显示正确的原文件?

无效的方法

以下是尝试过的几种创建Invoke-S的方法及结果:

methodnew_errorinvoke_errorstacktrace
iex function globalAttempted to divide by zero.at global:Invoke-S, …
function global: $sbMissing function body in…The term 'Invoke-S' is not recognized…at , C:\Users\Us…
New-Item Function:The term 'Invoke-S' is not recognized…at , C:\Users\Us…
New-Item global:Function:Cannot find drive. A dri…The term 'Invoke-S' is not recognized…at , C:\Users\Us…
Get-Variable function:Cannot find a variable w…The term 'Invoke-S' is not recognized…at , C:\Users\Us…

这些方法要么无法在调用者作用域中创建可用函数(错误信息为'Invoke-S' is not recognized),要么堆栈跟踪中无法正确指向原文件(错误信息为at global:Invoke-S, <No file>)。

对应的测试代码如下:

function New-GlobalFunction {
param(
    [Parameter(Mandatory)][string]   $ScriptPath  ,
    [Parameter(Mandatory)][string]   $FunctionName,
    [Parameter(Mandatory)][ValidateSet(
        'iex function global'      ,
        'function global: $sb'     ,
        'New-Item Function:'       ,
        'New-Item global:Function:',
        'Get-Variable function:'    )]$Method
)
switch ($Method) {
    'iex function global' {
        Invoke-Expression `
            -Command "function global:$FunctionName {
                        $(Get-Content $ScriptPath)
                     }"
    }
    'function global: $sb' {
        $sb =
            Get-Command           `
                -Name $ScriptPath |
            % ScriptBlock
        # function global:Invoke-S $sb
        throw 'Missing function body in function declaration.'
    }
    'New-Item Function:' {
        New-Item                       `
            -ErrorAction Stop          `
            -Path        Function:     `
            -Name        $FunctionName `
            -Value (Get-Command $ScriptPath |
                    % ScriptBlock            )
    }
    'New-Item global:Function:' {
        New-Item                    `
            -ErrorAction Stop       `
            -Path  global:Function: `
            -Name  $FunctionName    `
            -Value (Get-Command $ScriptPath |
                    % ScriptBlock            )
    }
    'Get-Variable function:' {
        Get-Variable          `
            -Name 'function:' `
            -ErrorAction Stop
    }
}}

$(foreach ($method in 'iex function global'         ,
                      'function global: $sb',
                      'New-Item Function:'          ,
                      'New-Item global:Function:'   ,
                      'Get-Variable function:'       ) {
    [pscustomobject]@{
        method       = $method
        new_error    = $(try {$null =
                              New-GlobalFunction       `
                                -ScriptPath  .\s.ps1   `
                                -FunctionName Invoke-S `
                                -Method       $method   }
                        catch {$_})
        invoke_error = ($e =
                       try {Invoke-S -ErrorAction Stop}
                       catch {$_}     )
        stacktrace   = $e.ScriptStackTrace
        e            = $e
    }
    Remove-Item Function:Invoke-S -ErrorAction SilentlyContinue
}) |
    Format-Table `
        -Property @{e='method'      ;width=25},
                  @{e='new_error'   ;width=25},
                  @{e='invoke_error';width=38},
                  @{e='stacktrace'  ;width=30}

参考资料

  • about_Functions
  • about_Scopes
  • about_Scripts

内容的提问来源于stack exchange,提问作者alx9r

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.16 07:20:58