参考函数时,PowerShell退出事件不起作用

Eth*_*han 4 powershell

我有一个.ps1脚本,一旦PowerShell会话结束,就需要一些代码来执行清理目的。我遇到的问题的最简单再现:

function ExitLogic(){
     Write-Host 'closing'
}
Write-Host 'started'
Register-EngineEvent `
    -SourceIdentifier ([System.Management.Automation.PsEngineEvent]::Exiting) `
    -Action { ExitLogic }
Run Code Online (Sandbox Code Playgroud)

ExitLogic永远不会发生。如果我exit在PowerShell会话中手动使用命令,则不是,如果单击X窗口按钮,如果在cmd.exe...中运行PowerShell,则不是。但是,如果我将Action参数从ExitLogic作为函数的引用更改为该函数Write-Host 'inline closing'就可以了。

mkl*_*nt0 5

tl; dr

-Action脚本块执行时(引擎退出时),该ExitLogic()功能将不再起作用。


给Ethan的注意:我为您提供的一些要点可能已经意识到-我在这里拼写出来是为了将来的读者。

一般要点:

  • *.ps1文件不在子进程中运行,因此退出脚本并不等同于整体退出PowerShell 引擎

  • 默认情况下,脚本在子范围内运行,因此其中定义的功能仅在脚本运行时才在范围内。

  • 在脚本块中只能引用在全局范围内定义的函数-Action

  • -Action脚本块执行时,许多常规的PowerShell功能不再可用从v6.2.0起撰写

    • 值得注意的是,PowerShell自身的输出流不再可用 -常规输出和错误消息不再打印。

    • 但是,您可以 Write-Host产生的显示输出(虽然外部用户收到通过标准输出),但要注意,如果还排出发动机关闭当前控制台窗口中,你甚至不会看到。Read-Host命令可以延迟闭合。

    • 似乎只有来自Microsoft.PowerShell.Utility模块的命令可用,而所有其他模块均已卸载-请参阅GitHub问题
  • 重要提示当PowerShell本身退出会话时(无论是正常退出还是通过触发的脚本终止错误throw,事件处理程序才能运行 - 如果通过关闭控制台/终端窗口间接终止PowerShell ,则事件处理程序将不会运行。

您的具体情况

  • 鉴于-Action当块被调用引擎退出,这恰好经过剧本完成后,它并没有看到ExitLogic功能,这地方了范围的。

    • 使一种方式ExitLogic在提供全球范围内是“点源”脚本(如. .\script.ps1),但要注意,只能全局范围; 从子作用域或模块作用域向全局作用域添加功能还需要做更多的工作。

以下代码段演示了这一点:

假设存在.\script.ps1以下内容的脚本:

function ExitLogic {
  Write-Host 'closing'
}

Write-Host 'started'

$null = Register-EngineEvent `
 -SourceIdentifier PowerShell.Exiting `
 -Action { 
   try { ExitLogic } catch { Write-Host $_ }
   Read-Host -Prompt 'Press ENTER to exit' 
 }
Run Code Online (Sandbox Code Playgroud)

注意:作为Get-Help Register-EngineEvent状态,下面的-SourceIdentifier值被支持:PowerShell.ExitingPowerShell.OnIdle,和PowerShell.OnScriptBlockInvoke,对应于enum-的值 [System.Management.Automation.PSEngineEvent] ; 显式地使用后者(如在OP中)可为您提供更多的类型安全性,但键入起来也比较麻烦。

警告以下命令退出正在运行的PowerShell会话;只是Read-Host使窗口保持打开状态的命令,允许您检查事件处理程序的Write-Host输出;按下时Enter,窗户关闭

  • 直接调用:
# Due to direct invocation, the -Action block does NOT see ExitLogic()
PS> .\script.ps1; exit
started
The term 'ExitLogic' is not recognized as the name of a cmdlet, function, 
script file, or operable program.
Check the spelling of the name, or if a path was included, verify that the path is correct and try again.
Press ENTER to exit: 
Run Code Online (Sandbox Code Playgroud)
  • 点源调用:
# Thanks to dot-sourcing in the global scope, the -Action block DOES see ExitLogic()
PS> . .\script.ps1; exit
started
closing
Press ENTER to exit: 
Run Code Online (Sandbox Code Playgroud)