11

The question applies to a script such as the following:

Script

#!/bin/sh

SRC="/tmp/my-server-logs"

echo "STARTING GREP JOBS..."
for f in `find ${SRC} -name '*log*2011*' | sort --reverse`
do
    (
        OUT=`nice grep -ci -E "${1}" "${f}"`
        if [ "${OUT}" != "0" ]
        then
            printf '%7s : %s\n' "${OUT}" "${f}"
        else
            printf '%7s   %s\n' "(none)" "${f}"
        fi
    ) &
done

echo "WAITING..."
wait

echo "FINISHED!"

Current behavior

Pressing Ctrl+C in console terminates the script but not the already running grep processes.

Ivivi Vavava
  • 723
  • 1
  • 8
  • 11

2 Answers2

16

Write a trap for Ctrl+c and in the trap kill all of the subprocesses. Put this before your wait command.

function handle_sigint()
{
    for proc in `jobs -p`
    do
        kill $proc
    done
}

trap handle_sigint SIGINT
Andrew
  • 7,286
  • 3
  • 28
  • 38
  • Thank you! Works great! Except that my bash expected `function` before `handle_sigint`. – Ivivi Vavava Oct 19 '11 at 07:48
  • I've added `function` to the script. – Andrew Oct 19 '11 at 08:01
  • Well, here's a problem: when the script is ran on **hundreds** of files, **not all** processes are started at once (which is good). Sadly, this leads to termination of **only a fraction** of processes on `Ctrl+C`. I suppose, **during termination** inside the trap loop, others are getting started. Suggestions? *(Moving the trap and its function before the main loop doesn't help)* – Ivivi Vavava Oct 19 '11 at 12:50
  • 1
    http://stackoverflow.com/questions/360201/kill-background-process-when-shell-script-exit looks useful. `trap "kill 0" SIGINT` works on my machine. – Andrew Oct 19 '11 at 13:36
  • Note: the first line of your script needs to read `#!/bin/bash`, not `#!/bin/sh`, for this to work. – Fabian Tamp May 19 '13 at 11:00
0

A simple alternative is using a cat pipe. The following worked for me:

echo "-" > test.text; 
for x in 1 2 3; do 
    ( sleep $x; echo $x | tee --append test.text; ) & 
done | cat

If I press Ctrl-C before the last number is printed to stdout. It also works if the text-generating command is something that takes a long time such as "find /", i.e. it is not only the connection to stdout through cat that is killed but actually the child process.

For large scripts that make extensive use of subprocesses the easiest way to ensure the indented Ctrl-C behaviour is wrapping the whole script into such a subshell, e.g.

#!/usr/bin/bash
(
    ...
) | cat

I am not sure though if this has the exactly same effect as Andrew's answer (i.e. I'm not sure what signal is sent to the subprocesses). Also I only tested this with cygwin, not with a native Linux shell.

kdb
  • 4,098
  • 26
  • 49