Skip to content
Advertisement

bash: Possible to require double Ctrl-c to to exit a script?

End-goal: BASH script that is waiting for background jobs to finish does not abort on first Ctrl-c; instead, it requires a second Ctrl-c to quit.

I’m well aware of how the BASH-builtin trap works. You can either:

  1. Use it to ignore a signal altogether (e.g., trap '' 2) … or

  2. Use it to have arbitrary commands executed before a signals original function is allowed to happen (e.g., trap cmd 2, where cmd is run before the parent script will be interrupted due to SIGINT)

So the question boils down to this:

How can I effectively combine 1 & 2 together, i.e., prevent the end-result a signal would lead to (1 — e.g., stop script cancelling due to SIGINT) while also making that signal cause something else (2 — e.g., increment a counter, check the counter and conditionally either print a warning or exit).

Put more simply:

How can I make a signal do something else entirely; not just insert a job before it does its thing.

Here’s some example code to demonstrate what I’m aiming at; however, it of course doesn’t work — because trap can only do 1 or 2 from above.

JavaScript

So this is the output you end up getting when you press Ctrl-c once.

JavaScript

Of course I could modify that to clean up the jobs on the first Ctrl-c, but that’s not what I want. I want to stop BASH from quiting after the first trap is triggered … until it’s triggered a second time.

PS: Target platform is Linux (I couldn’t care less about POSIX compliance) with BASH v4+

Advertisement

Answer

A colleague (Grega) just gave me a solution which … well I can’t believe I didn’t think of it first.

“My approach would … be to lay it off for long enough, possibly forever, using a function that just never returns or something (another wait?), so that the second handler can do its job properly.”

For the record, wait would not work here. (Recursive.) However, adding a sleep command to my original code’s cleanup_bg_jobs() function would take care of it .. but would lead to orphaned processes. So I leveraged process groups to ensure that all children of the script really do get killed. Simplified example for posterity:

JavaScript
User contributions licensed under: CC BY-SA
5 People found this is helpful
Advertisement