Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Powershell Finally block skipped with Ctrl-C

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.

like image 238
b_c Avatar asked Jul 14 '17 13:07

b_c


2 Answers

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.

like image 61
Vesper Avatar answered Nov 15 '22 07:11

Vesper


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.

  • "If the command is the last command in the pipeline, the objects are displayed in the console." - appears this statement is false within a finally block. However, passing to Out-Host explicitly will yield desired output.
  • On Try-Catch-Finally note
    • The quoted section is confusing as it applies to unhandled objects sent to the pipeline.
    • Objects sent to the pipeline and handled within a Finally block are fine.
    • It talks about "even if the Finally block has ran" but the 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
}
like image 23
G42 Avatar answered Nov 15 '22 06:11

G42