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
shrun 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/shfrom 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
pingfrom the linked example, this script won't exit with a 0 status upon a signal, so a simplewhile ...; .do ... /script || break; donewill 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