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

时间:2017-11-24 15:44:45

标签: powershell

我有一个.ps1脚本,一旦PowerShell会话结束,需要执行一些代码以进行清理。最简单地复制我遇到的问题:

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

ExitLogic永远不会发生。如果我在PowerShell会话中手动使用exit命令,而不是单击X窗口按钮,如果我在cmd.exe内运行PowerShell,则不是...我在失利。 但是,如果我将Action参数从引用ExitLogic作为函数更改为Write-Host 'inline closing',那么它确实可以正常工作。

1 个答案:

答案 0 :(得分:4)

<强> TL;博士

-Action脚本块执行时(引擎退出时),ExitLogic()函数不再在范围内。

关于Ethan的注意事项:我可能已经意识到的一些要点 - 为了未来的读者的利益,我在这里拼写出来。

一般要点:

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

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

  • 只有全局范围内定义的功能才能在-Action脚本块中引用。

  • -Action脚本块执行时,大部分常规PowerShell功能不再可用从v6.2.0开始编写 < / p>

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

    • 但是,你可以 使用Write-Host生成显示输出(虽然外部来电者将会通过stdout接收,但请注意如果引擎退出也会关闭当前控制台窗口,您甚至不会看到它。 A Read-Host命令可以延迟结束。

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

您的具体案例

  • 鉴于在引擎退出时调用-Action块,完成脚本后发生,它 not 请参阅ExitLogic函数,该函数已超出范围。

    • 全球范围内提供ExitLogic的一种方法是使用#dot; dot-source&#34;您的脚本(例如. .\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' 
 }

注意:正如Get-Help Register-EngineEvent所述,支持以下-SourceIdentifier值:PowerShell.ExitingPowerShell.OnIdlePowerShell.OnScriptBlockInvoke,它们对应于enum- 的值,如 [System.Management.Automation.PSEngineEvent] class;明确地使用后者(如在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: 
  • Dot-Sourced调用:
# Thanks to dot-sourcing in the global scope, the -Action block DOES see ExitLogic()
PS> . .\script.ps1; exit
started
closing
Press ENTER to exit: