Bash script: can not properly handle SIGTSTP
The shell does not execute the trap until the currently executing process terminates. (at least, that is the behavior of bash 3.00.15). If you send SIGINT via ^c, it is sent to all processes in the foreground process group; if the program currently executing receives it and terminates then bash can execute the trap. Similarly with SIGTSTP via ^z; bash receives the signal but does not execute the trap until the program that was being run terminates, which it does not do if it takes the default behavior and is suspended. Try replacing ...
with a simple read f
and note that the trap executes immediately.
Bash: Why does parent script not terminate on SIGINT when child script traps SIGINT?
New answer:
This question is far more interesting than I originally suspected. The answer is essentially given here:
What happens to a SIGINT (^C) when sent to a perl script containing children?
Here's the relevant tidbit. I realize you're not using Perl, but I assume Bash is using C's convention.
Perl’s builtin system function works just like the C system(3)
function from the standard C library as far as signals are concerned.
If you are using Perl’s version of system() or pipe open or backticks,
then the parent — the one calling system rather than the one called by
it — will IGNORE any SIGINT and SIGQUIT while the children are
running.
This explanation is the best I've seen about the various choices that can be made. It also says that Bash does the WCE approach. That is, when a parent process receives SIGINT, it waits until its child process returns. If that process handled exited from a SIGINT, it also exits with SIGINT. If the child exited any other way it ignores SIGINT.
There is also a way that the calling shell can tell whether the called
program exited on SIGINT and if it ignored SIGINT (or used it for
other purposes). As in the WUE way, the shell waits for the child to
complete. It figures whether the program was ended on SIGINT and if
so, it discontinue the script. If the program did any other exit, the
script will be continued. I will call the way of doing things the
"WCE" (for "wait and cooperative exit") for the rest of this document.
I can't find a reference to this in the Bash man page, but I'll keep looking in the info docs. But I'm 99% confident this is the correct answer.
Old answer:
A nonzero exit status from a command in a Bash script does not terminate the program. If you do an echo $?
after ./script2.sh
it will show 130. You can terminate the script by using set -e
as phs suggests.
$ help set
...
-e Exit immediately if a command exits with a non-zero status.
Can successfully trap CTRL-Z, but not SIGTSTP
First you must not trap SIGTSTP for the shell itself (it should ignore it), only for its child. Second if you really want to write a job controller shell, you need to manage children with the help of process group and correctly set the foreground group. Writing a shell that behave correctly with job control is a heavy task. Read POSIX standard about shells, groups, sessions, terminal control.
About your current problem. If your sub process does an exec then each handled signal is reset to its default behavior. This is because exec recovers the old code with the new one, so the previously set handler is no more available. Now you must let the child behave normally against TSTP
and just let the parent track its status either with a synchronous call to wait/waitpid
or with the asynchronous help of SIGCHLD
. When the child stops or terminates, the parent is able to see it in the returned status (WIFEXITED
, WIFSIGNALED
, WIFSTOPPED
).
How do I stop a signal from killing my Bash script?
I believe you're looking for SIGTERM
:
Example:
#! /bin/bash
trap -- '' SIGINT SIGTERM
while true; do
date +%F_%T
sleep 1
done
Running this example cTRL+C
won't kill it nor kill <pid>
you can however kill it with kill -9 <pid>
.
If you don't want CTRL+Z
to interrupt use: trap -- '' SIGINT SIGTERM SIGTSTP
Can Bash register TSTP/CONT traps for its own suspend command?
Short answer, no. suspend
apparently uses an actual SIGSTOP
(not SIGTSTP
), which I believe (like SIGKILL
) cannot be caught - someone please correct me if I'm wrong. Your trap for SIGTSTP
is working fine, though. :)
Using your outer$
and inner$
"prompts" to indicate state and hopefully reduce confusion, though obviously nothing is setting them that way. My prompts were different enough to be clear, but too noisy to use here.
outer$ bash
inner$ trap 'echo TSTP received' TSTP # this catches - and effectively ignores - TSTP
inner$ kill -TSTP $$ # this raises a TSTP to the current process
TSTP received
inner$ suspend # this is apparently not sending a TSTP
[1]+ Stopped bash
outer$ fg 1 # this resumes the suspended job back into foreground
inner$ echo $$
1402
inner$ suspend
[1]+ Stopped bash
outer$ kill -CONT 1402 # this also wakes it up
inner$
How to trap CTRL+Z in Linux POSIX shell script; possible or not?
To ignore Ctrl+Z typed from a terminal, you can do:
trap '' TSTP
# or possibly equivalently
trap '' 18
trap '' 20
trap '' 24
As noted by others, there are other signals that cannot be blocked, such as SIGKILL and SIGSTOP.
Many shells map Ctrl+\ to SIGKILL and Ctrl+C to SIGINT.
stty -a
should show settings for sending "susp" (SIGTSTP), "intr" (SIGINT) and "quit" (SIGKILL). These keystrokes can be rebound by the user if desired.
Editor's notes:
You cannot use
SIGTSTP
pseudonym in a POSIX shell script.Even if you know for sure what exact signal number your terminal sends, if writing a script for general use, you ought to block all those three possibilities.
ShellCheck warning, and I quote:
Trapping signals by number is not well defined. Prefer signal names. [SC2172]
Means these signal numbers can vary between platforms, try 15 for SIGTERM and you will see no warning at all as that one is POSIX standardized. Ergo, you might want to disable these after you've tested your code with:
# shellcheck disable=SC2172
Link to wiki: https://github.com/koalaman/shellcheck/wiki/SC2172
Creating my own shell. Handling Ctrl-Z and then sending SIGCONT closes the process instead of continue it
It seems like the problem is caused because of process groups. I did only create different process groups for background jobs but since you cannot change the process group of a child after it executed exec command, you better do it at the beginning before exec call. Now, the problem is solved thanks to "@that other guy" and "@John Bollinger".
Related Topics
Cdc_Acm: Failed to Set Dtr/Rts - Can Not Communicate with Usb Cdc Device
Recording from Alsa - Understanding Memory Mapping
How to Configure Gitlab as a Subdomain in Nginix.Conf
Can a Program Read Its Own Elf Section
Sdl Configuration in Eclipse Ide
Qxcbconnection: Could Not Connect to Display Aborted, When Installing Qt on Linux
Linux Patch Ignoring Line Numbers
Naming Convention for Posix Flags
How to View Dask Dashboard When Running on a Virtual Machine
Linux Umask for Sudo and Apache
Simplest Way to Build Dotnet Sdk Project Requiring Net461 on Macos
What Does "Private_Dirty" Memory Mean in Smaps
Jetty Bash Script Works Only with Root User
Change Conda Default Pkgs_Dirs and Envs Dirs
How Linux Scheduler Schedules Processes on Multi-Core Processors