I'm writing a monitoring script in Powershell using a Try/Finally to log a message should the script end. The script is intended to run indefinitely, so I want a way to track unintended exiting.
Every other StackOverflow post and Help page I've checked states:
A Finally block runs even if you use CTRL+C to stop the script. A Finally block also runs if an Exit keyword stops the script from within a Catch block.
In practice, I have not found this to be true. I'm using the following contrived example to test this out:
Try {
While($True) {
echo "looping"
Start-Sleep 3
}
} Finally {
echo "goodbye!"
pause
}
The Finally
block here is skipped every time after a Ctrl+C (no echo, no pause), both when running as a saved script or when executing through the built-in Powershell ISE. The only output I ever get is:
looping
looping
...(repeat until Ctrl-C)
I've clearly missed something, but I have no idea what it is, especially in a code snippet as small as this.
The proper answer is that Ctrl+C breaks pipeline, as is also stated in that link, and echo
uses the pipeline to process its output. Therefore, once you Ctrl+C, writing to the pipeline causes the script block to err, and to not process any further commands. Therefore, do not use commands that send output to stdout directly, and there is a lot of them that indirectly use pipeline. Write-Host
, on the other hand, does not use the pipeline, and thus does not throw an error.
Functional Code
This will give you the behaviour I believe you're after:
Try {
While($True) {
echo "looping"
Start-Sleep 3
}
} Finally {
Write-Host "goodbye!"
pause
}
References
Write-Output/echo - Synopsis
Sends the specified objects to the next command in the pipeline. If the command is the last command in the pipeline, the objects are displayed in the console.
Write-Host - Synopsis
Writes customized output to a host.
Try-Catch-Finally - Syntax note
Note that pressing CTRL+C stops the pipeline. Objects that are sent to the pipeline will not be displayed as output. Therefore, if you include a statement to be displayed, such as "Finally block has run", it will not be displayed after you press CTRL+C, even if the Finally block ran.
Explanation
The key, as per TheIncorrigible1's comment and Vesper's answer is that the pipeline is stopped. But this is not because of an error in Write-Output
. And I don't find it is a satisfying explanation on its own.
Out-Host
explicitly will yield desired output.Finally
block are fine.pause
does not run if preceded by a Write-Output
.More Code
A few things ran in the Finally
block to investigate behaviour, with comments as to what happens.
} Finally {
Write-Output "goodbye!" | Out-Default # works fine
pause
}
} Finally {
Write-Output "goodbye!" | Out-Host # works fine
pause
}
} Finally {
pause # works fine
Write-output "goodbye!" # not executed
}
} Finally {
try{
Write-Output "goodbye!" -ErrorAction Stop
}catch{
Write-Host "error caught" # this is not executed.
} # $error[0] after script execution is empty
pause
}
} Finally {
try{
ThisCommandDoesNotExist
}catch{
Write-Host "error caught" # this is executed
} # $error[0] contains CommandNotFoundException
pause
}
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