To jump straight in:

while true; do
    #---  MENU LOGIC HERE, stick response in $MENUEXIT

    #----Deal with responses here

    if [ $S1 == $MENUEXIT ];
            tail -f /path-to-file
            sleep 2

I've tried to keep this as simple as possible, if you need more, let me know.

Basically, I am sticking a menu in a loop, so, if there is incorrect user input or a command finishes, it is meant to re-display the menu.

It worked fine, until I added the tail command.

If I choose the tail option from the menu, the tail command gets launched fine - but, if I hit Ctrl + C, I would like tail to terminate and the menu to be displayed, but it instead both terminates tail AND the script.

I have tried various things such as continues/traps and more, but, I have hit a brick wall and would like some help please?!


When you send a signal like SIGINT (CtrlC) or SIGSTOP (CtrlZ) from a terminal, the signal is sent to the foreground process group. That is, the group comprising of the foreground job (your script), and any child processes it has in the foreground (the tail command). This causes all these processes to exit (or do whatever it is they do by handling the signal). You can test the difference by sending a signal using the kill command:

In one terminal, execute this script (I'm calling it test in the following commands):

#! /bin/bash

while true
    if [[ $1 = a ]]
        echo no
        sleep 2

In another, execute these commands:

pstree -ps $(pgrep -f test)
pkill -f test
pstree -ps $(pgrep -f yes)

The output of the commands would be something like this:

# No output for pkill

(The actual numbers and programs may vary, but the first init will always have pid 1.)
As you can see, the yes command wasn't killed, and got attached to init because its parent was killed. It's still merrily printing ys on the first terminal. So kill it with pkill -f yes. Now repeat the experiment, with one change. Instead of pkill -f test, do:

kill -INT -25165 
# Use the pid of test.sh given in parentheses in the output of pstree 
# instead of 25165

Note the leading -. In Linux, for the kill command, -25165 is the process group, whose leader has pid 25165. Thus, this command is the equivalent of sending an interrupt from the terminal.

Of course, the exact behaviour depends on the configuration of the TTY, the login shell and so on. This is as far as my understanding goes. Further reading:

I suggest:

  1. Sending the tail to the background
  2. Trap the signal
  3. Kill the background process.
  4. Remove the trap

An example:

#! /bin/bash

kill_jobs ()
    kill %1

while true
    if [[ $1 = "a" ]]
        trap kill_jobs INT
        tail -f /var/log/syslog &
        wait %%
        shift #You don't need to shift, I just didn't want loop tail on this example
        trap - INT
        echo some output
        sleep 2

This can become very powerful, by using different functions for trapping, and each doing their own clean up work.

  • Hi, Thanks for this and I'll try it shortly. Os it basically killing the entire process tree when I do control + c or is something else going on? It is great you provided an answer, but, I'm trying to get a deeper understanding of Linux so I can understand why these things happen the way they do! – wilhil Aug 9 '14 at 17:44
  • that is great! Thanks for both the explanation and the fix! Works great and now I fully understand why it is happening! :) – wilhil Aug 10 '14 at 15:21
  • 1
    @Sneetsher added. – muru Jan 22 '16 at 9:36

Your Answer

By clicking "Post Your Answer", you acknowledge that you have read our updated terms of service, privacy policy and cookie policy, and that your continued use of the website is subject to these policies.

Not the answer you're looking for? Browse other questions tagged or ask your own question.