考虑一个名为 s.ps1
且包含内容的
PowerShell 脚本
1/0
和假设函数
NAME
New-GlobalFunction
SYNOPSIS
Creates a new function in global scope from a script.
SYNTAX
New-GlobalFunction [-ScriptPath] <string> [-FunctionName] <string>
我希望能够使用 Invoke-S
定义 global
函数
New-GlobalFunction
,以便
Invoke-S
生成错误位置信息,显示类似于以下内容的正确文件:
PS C:\> New-GlobalFunction -ScriptPath .\s.p1 -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
请注意原始文件的提及c.ps1
。PowerShell 的架构表明这应该是可能的,因为
脚本和函数只是脚本块:
在PowerShell编程语言中,脚本块是可以用作单个单元的语句或表达式的集合。语句集合可以用大括号 ({}) 括起来、定义为函数或保存在脚本文件中。事实上,
s.ps1
定义的脚本块可以通过
Get-Command .\s.ps1 | % ScriptBlock
访问。 上面具有正确位置信息的错误是通过调用该脚本块产生的。 事实上,该脚本块可以使用命令变成local函数
New-Item -Path Function: -Name Invoke-S -Value (Get-Command .\s.ps1 | % ScriptBlock)
但是,我还没有成功创建这样一个global函数。
有没有办法定义New-GlobalFunction
,使得
-ScriptPath
文件中定义的脚本块产生的错误在其错误位置消息中包含正确的文件?无效的方法
Invoke-S
的不同方法。 它输出以下内容:
method new_error invoke_error stacktrace
------ --------- ------------ ----------
iex function global Attempted to divide by zero. at global:Invoke-S, <No file>…
function global: $sb Missing function body in… The term 'Invoke-S' is not recognized… at <ScriptBlock>, C:\Users\Us…
New-Item Function: The term 'Invoke-S' is not recognized… at <ScriptBlock>, C:\Users\Us…
New-Item global:Function: Cannot find drive. A dri… The term 'Invoke-S' is not recognized… at <ScriptBlock>, C:\Users\Us…
Get-Variable function: Cannot find a variable w… The term 'Invoke-S' is not recognized… at <ScriptBlock>, 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}
参考文献命名空间变量表示法,似乎有效:
$FunctionName = 'Invoke-S'
$ScriptPath = '.\s.ps1'
Invoke-Expression @"
`${function:global:$FunctionName} = (Get-Command "$ScriptPath").ScriptBlock
"@
function New-GlobalFunction {
param(
[Parameter(Mandatory)]
[string] $ScriptPath,
[Parameter(Mandatory)]
[string] $FunctionName,
[Parameter()]
[switch] $Force
)
$command = Get-Command $ScriptPath -ErrorAction Stop
$null = $PSCmdlet.SessionState.InvokeProvider.Item.New(
'function:global:',
$FunctionName,
'Function',
{ & $command }.GetNewClosure(),
$Force.IsPresent)
}