10

Considering a custom command line software (e.g. loopHelloWorld) which detect ctrl+C for nice shutdown. How to pipe the answer without losing the Ctrl+C?

$ loopHelloWorld
    - Ctrl+C to nicely shutdown

But with pipe, the pipe kill the software without nice shutdown

$ loopHelloWorld | 
    while IFS= read -r line; do
        echo "$line"
    done

Example

ping example.com |
  while IFS= read -r line; do
    echo "$line"
  done

2 Answers 2

14

Ctrl+C causes a SIGINT to be sent to all processes in the pipeline (as they're all run in the same process group that correspond to that foreground job of your interactive shell).

So in:

loopHelloWorld | 
  while IFS= read -r line; do
    echo "$line"
  done

Both the process running loopHelloWorld and the one running the subshell that runs the while loop will get the SIGINT.

If loopHelloWorld writes the Ctrl+C to nicely shutdown message on its stdout, it will also be written to the pipe. If that's after the subshell at the other end has already died, then loopHelloWorld will also receive a SIGPIPE, which you'd need to handle.

Here, you should write that message to stderr as it's not your command's normal output (doesn't apply to the ping example though). Then it wouldn't go through the pipe.

Or you could have the subshell running the while loop ignore the SIGINT so it keeps reading the loopHelloWorld output after the SIGINT:

loopHelloWorld | (
  trap '' INT
  while IFS= read -r line; do
    printf '%s\n' "$line"
  done
)

that would however cause the exit status of the pipeline to be 0 when you press Ctrl+C.

Another option for that specific example would be to use zsh or ksh93 instead of bash. In those shells, the while loop would run in the main shell process, so would not be affected by SIGINT.

That wouldn't help for loopHelloWorld | cat though where cat and loopHelloWorld run in the foreground process group.

1
  • 1
    To pipe java cli program and use graceful shutdown hooks after CTRL+C, you can start it like this java -cp myGreatApp-1.0-SNAPSHOT.jar sk.Main | (trap '' INT; tee logfile.txt) in bash. This solution is described in unix.stackexchange.com/questions/213274/… question.
    – Lubo
    Nov 5, 2020 at 21:19
2

Use a trap:

#! /bin/bash

trap handleInt SIGINT

interrupted=0

function handleInt {
    echo "Interrupted..."
    interrupted=1
}


while true
do
    [[ interrupted -ne 0 ]] && { echo "Ctrl-C caught, exiting..." ; exit ; }
    echo "Sleeping..."
    sleep 1
    read -r line
    echo "$line"
done
2
  • This seem to capture the Ctrl+C, but it does not cancel the sigint neither propagate it to the loopHelloWorld Nov 27, 2017 at 10:26
  • Improved sample code...
    – xenoid
    Nov 27, 2017 at 11:22

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .