可以从 .ps1 脚本的脚本块在全局范围内创建函数吗?

问题描述 投票:0回答:2

考虑一个名为 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}
参考文献

  • 关于_功能
  • 关于_范围
  • 关于_脚本
powershell function scope global
2个回答
2
投票
以下使用

命名空间变量表示法,似乎有效:

$FunctionName = 'Invoke-S' $ScriptPath = '.\s.ps1' Invoke-Expression @" `${function:global:$FunctionName} = (Get-Command "$ScriptPath").ScriptBlock "@
    

0
投票
另一种方法是将

CommandInfo

 存储在值脚本块中。

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) }
    
© www.soinside.com 2019 - 2024. All rights reserved.