Bash Script: Can Not Properly Handle Sigtstp

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



Leave a reply



Submit