I have a .ps1
script that needs some code to execute for cleanup purposes once the PowerShell session ends. Simplest reproduction of the problem I am having:
function ExitLogic(){
Write-Host 'closing'
}
Write-Host 'started'
Register-EngineEvent `
-SourceIdentifier ([System.Management.Automation.PsEngineEvent]::Exiting) `
-Action { ExitLogic }
The ExitLogic never happens. Not if I manually use the exit
command within my PowerShell session, not if I click the X
window button, not if I run PowerShell within a cmd.exe
... I'm at a loss. But, if I change the Action
parameter from referencing ExitLogic
as a function to just Write-Host 'inline closing'
then it does work.
tl;dr
Depending on how your script is invoked, the -Action
script block may not see your ExitLogic
function - to ensure that it is visible there, define the function in the global scope.
General points:
*.ps1
files do NOT run in a child process, so exiting the script is NOT tantamount to exiting the PowerShell engine as a whole.
scripts by default run in a child scope, so functions defined therein are only in scope while the script is running.
Only functions defined in the global scope can be referenced in an -Action
script block, because it runs in a dynamic module (an in-memory module, which, like persisted modules, sees only definitions from the global scope).
By the time the -Action
script block executes, much of regular PowerShell functionality is no longer available.Written as of v6.2.0
Notably, PowerShell's own output streams cannot be used anymore - regular output and error messages no longer print.
However, you can use Write-Host
to produce display output (though an outside caller will receive that via stdout), but note that if the engine exiting also closes the current console window, you won't even get to see that. A Read-Host
command can delay the closing.
It seems that only commands from the Microsoft.PowerShell.Utility
module are available, whereas all other modules have already been unloaded - see this GitHub issue.
Important: The event handler only gets to run if PowerShell itself exits the session (whether normally or via a script-terminating error triggered with throw
) - if you terminate PowerShell indirectly by closing the console / terminal window, the event handler won't run - see this answer for details.
Your specific case:
Unless you happen to call your script file via the -File
parameter (as opposed to -Command
) of the PowerShell CLI (powershell.exe
for Windows PowerShell, pwsh
for PowerShell (Core) 7+), the -Action
script block does not see the ExitLogic
function, because it is then only defined in the script file's scope, not the global one.
ExitLogic
available in the global scope is to "dot-source" your script (e.g., . .\script.ps1
), but note that only works from the global scope (this is essentially what the -File
CLI parameter does - it dot-sources the script file in the global scope); more work is needed to add a function to the global scope from a child scope or module scope.The following snippet demonstrates this:
Let's assume the existence of script .\script.ps1
with the following content:
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'
}
Note: As Get-Help Register-EngineEvent
states, the following -SourceIdentifier
values are supported: PowerShell.Exiting
, PowerShell.OnIdle
, and PowerShell.OnScriptBlockInvoke
, which correspond to the value of the enum-like [System.Management.Automation.PSEngineEvent]
class; using the latter explicitly (as in the OP) gives you more type safety, but is also more cumbersome to type.
Caveat: The following sample commands exit the running PowerShell session; it is only the Read-Host
command that keeps the window open, allowing you to inspect the event handler's Write-Host
output; on pressing Enter, the window closes.
powershell.exe -Command "& .\script.ps1 ..."
from the outside):# 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:
powershell.exe -File script.ps1 ...
from the outside):# Thanks to dot-sourcing in the global scope, the -Action block DOES see ExitLogic()
PS> . .\script.ps1; exit
started
closing
Press ENTER to exit:
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With