In the interactive console, pressing ^C
on zenity --info & fg
closes the Zenity window. One can only use zenity --info & wait
in a script. But ^C
doesn't close the Zenity window in this case. Is there any way to make wait
behave more like fg
and make ^C
work?

- 914
- 1
- 7
- 16
2 Answers
The easiest thing you can do is to trap the SIGINT
(^C) and kill the background process from the handler:
#! /bin/sh
trap 'kill "$!"; exit 1' INT QUIT
zenity --info &
wait
See this and other similar answers for an explanation for this behavior.
When calling your script from some shells like bash
which implement the so-called "Wait and Cooperative Exit" (see here for a discussion), the expected way for a program to cleanly exit upon receiving a signal is by resetting the handler and then killing itself with the same signal:
#! /bin/sh
termsig(){ trap - "$1"; kill "$!"; kill -s "$1" "$$"; }
trap 'termsig INT' INT
trap 'termsig QUIT' QUIT
zenity --info &
wait
All this is of course quite gross and won't be able to do anything about children processes that zenity
itself may have spawned, and will have to be adapted for more than one background process. The more obvious solution (trap - INT; zenity --info) &
will not work with the /bin/sh
from any Debian-derived distros (Ubuntu, etc) or any busybox-based systems.
Another solution would be run your program through a wrapper that allows you to reset the SIGINT
disposition to default, eg. with perl
:
#! /bin/sh
perl -e '$SIG{INT} = $SIG{QUIT} = "DEFAULT"; exec @ARGV' zenity --info &
wait
But if you go that way, you may just as well re-write your whole script in perl
or python
;-)
In a non-interactive sh
, commands run asynchronously with &
have their SIGINT and SIGQUIT signals ignored.
That's a POSIX requirement, and goes back to the time when terminal job control (tcsetpgrp()
...) didn't exist. Today, it generally gets in the way.
Another POSIX requirement and that comes from the same origin is that when a signal was ignored upon shell startup, it cannot be un-ignored with trap
. Again, nowadays, that's unwanted and annoying behaviour.
zsh
implements that latter requirement only partly. a trap - SIGNAL
to restore the default disposition of an ignored SIGNAL
is ignored, but trap handler SIGNAL
installs the handler
for SIGNAL even when that SIGNAL was ignored upon startup, so one can still restore the default disposition if they really want to with trap : SIGNAL; trap - SIGNAL
.
As long as SIGINT was not ignored in the script (like when the script was run in background), you can do:
#! /bin/sh -
(trap - INT QUIT; exec zenity) & wait
To restore the default disposition for SIGINT and SIGQUIT.
However, though POSIX explicitly says it should work, it doesn't with a few sh
implementations. In particular, not with dash
and most other ash
derivatives, ksh93
, bosh
or yash
. So you may want to change the she-bang to #! /bin/bash -
or #! /bin/zsh -
or #! /bin/mksh -
to make sure you have a POSIX-compliant shell in this instance.
If SIGINT was ignored upon startup of the shell, you'll need to use zsh
to restore their disposition:
#! /bin/zsh -
trap : INT QUIT # noop handler to unignore
trap - INT QUIT # restore default disposition for the shell
(trap - INT QUIT; zenity) & wait
Example (here on Linux where the signal dispositions are exposed as bitmaps in /proc/<pid>/status
):
$ sh -c 'grep SigIgn /proc/self/status'
SigIgn: 0000000000000000
$ sh -c 'grep SigIgn /proc/self/status & wait'
SigIgn: 0000000000000006 # (1<<(SIGINT-1) | 1<<(SIGQUIT-1))
$ zsh -c '(trap - INT QUIT; exec grep SigIgn /proc/self/status) & wait'
SigIgn: 0000000000000000
$ bash -c '(trap - INT QUIT; exec grep SigIgn /proc/self/status) & wait'
SigIgn: 0000000000000000
$ mksh -c '(trap - INT QUIT; exec grep SigIgn /proc/self/status) & wait'
SigIgn: 0000000000000000
$ dash -c '(trap - INT QUIT; exec grep SigIgn /proc/self/status) & wait'
SigIgn: 0000000000000006
$ ksh93 -c '(trap - INT QUIT; exec grep SigIgn /proc/self/status) & wait'
SigIgn: 0000000000000006
$ yash -c '(trap - INT QUIT; exec grep SigIgn /proc/self/status) & wait'
SigIgn: 0000000000000006
$ bosh -c '(trap - INT QUIT; exec grep SigIgn /proc/self/status) & wait'
SigIgn: 0000000000100006 # also SIGTTIN
$ (bash -c 'grep SigIgn /proc/self/status' & wait)
SigIgn: 0000000000000006
$ (bash -c 'trap - INT QUIT; grep SigIgn /proc/self/status' & wait)
SigIgn: 0000000000000006
$ (zsh -c 'grep SigIgn /proc/self/status' & wait)
SigIgn: 0000000000000002 # not sure why the SIGQUIT was unignored
$ (zsh -c 'trap - INT QUIT; grep SigIgn /proc/self/status' & wait)
SigIgn: 0000000000000002
$ (zsh -c 'trap : INT QUIT; trap - INT QUIT; grep SigIgn /proc/self/status' & wait)
SigIgn: 0000000000000000

- 544,893
-
-
@mosvy, it works with mksh, bash and zsh and should work in POSIX compliant implementations. I've added a note. – Stéphane Chazelas Apr 24 '19 at 06:45
-
The first sentence should probably be "In a
sh
run without job control enabled (as in a script or subshell), etc". Also, you really should change that shebang to#! /bin/bash
-- that won't work with the/bin/sh
from debian, ubuntu, busybox, etc; no matter if "entry to a non-interactive shell" in the standard is interpreted to include subshells or not. – Apr 24 '19 at 07:10
ping
from the linked example, this script won't exit with a 0 status upon a signal, so a simplewhile ...; .do ... /script || break; done
will do. Committing seppuku (by resetting the handler and re-sending itself the signal) won't help with that anyway and may run into other issues. – Apr 24 '19 at 13:51