Gracefully stopping in Powershell
Asked Answered
C

4

50

How do I catch and handle Ctrl+C in a PowerShell script? I understand that I can do this from a cmdlet in v2 by including an override for the Powershell.Stop() method, but I can't find an analog for use in scripts.

I'm currently performing cleanup via an end block, but I need to perform additional work when the script is canceled (as opposed to run to completion).

Cacomistle answered 10/11, 2009 at 19:49 Comment(1)
Possible duplicate of Is there a way to catch ctrl-c and ask the user to confirm?Lucretialucretius
M
15

You could use the method described on here on PoshCode

Summary:

Set

[console]::TreatControlCAsInput = $true

then poll for user input using

if($Host.UI.RawUI.KeyAvailable -and (3 -eq  
    [int]$Host.UI.RawUI.ReadKey("AllowCtrlC,IncludeKeyUp,NoEcho").Character))
Mirnamirror answered 24/11, 2009 at 13:4 Comment(1)
Wow, that's crazy. I ended up just rewriting my script as a cmdlet in C# so I could override Stop().Cacomistle
D
79

The documentation for try-catch-finally says:

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.

See the following example. Run it and cancel it by pressing ctrl-c.

try
{
    while($true)
    {
        "Working.."
        Start-Sleep -Seconds 1
    }
}
finally
{
    write-host "Ended work."
}
Dalrymple answered 3/4, 2013 at 13:40 Comment(4)
The is true, but Finally will run not only because Exit was called, but also if the Try block succeeds. As I indicated in my question, I need to perform additional work when the script is canceled (as opposed to run to completion).Cacomistle
@fatcat111 use a Boolean flag. For example, set $didcomplete = $true at the end of your try and check it with if ($didcomplete) { Write-Host "Ended work." } inside the finallyPhotoflash
Unfortunately, the finally block is not executed if the PowerShell console window is closed.Lanfri
The finally block is also called when stopping a remote PSSession invoke-command or Job!Barrada
M
15

You could use the method described on here on PoshCode

Summary:

Set

[console]::TreatControlCAsInput = $true

then poll for user input using

if($Host.UI.RawUI.KeyAvailable -and (3 -eq  
    [int]$Host.UI.RawUI.ReadKey("AllowCtrlC,IncludeKeyUp,NoEcho").Character))
Mirnamirror answered 24/11, 2009 at 13:4 Comment(1)
Wow, that's crazy. I ended up just rewriting my script as a cmdlet in C# so I could override Stop().Cacomistle
C
3

Here is recent, working solution. I use the if part in a loop where I need to control the execution interruption (closing filehandles).

    [Console]::TreatControlCAsInput = $true # at beginning of script

    if ([Console]::KeyAvailable){

        $readkey = [Console]::ReadKey($true)

        if ($readkey.Modifiers -eq "Control" -and $readkey.Key -eq "C"){                
            # tasks before exit here...
            return
        }

    }

Also note that there is a bug which leads KeyAvailable to be true upon start of scripts. You can mitigate by read calling ReadKey once at start. Not needed for this approach, just worth knowing in this context.

Clarindaclarine answered 21/12, 2021 at 21:26 Comment(0)
B
2

There is also a Stopping property on $PSCmdlet that can be used for this.

Brut answered 22/7, 2017 at 9:37 Comment(1)
Stopping is false even when stopping per PowerShell/PowerShell#6322.Warsle

© 2022 - 2024 — McMap. All rights reserved.