Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Bash script doesn't catch SIGINT while in read loop

Tags:

bash

shell

I have several scripts with while read line loops that don't execute my cleanup function when I press Ctrl C. For example:

#!/bin/bash

cleanup() {
  stty echo
  exit 0
}

trap cleanup SIGINT SIGHUP SIGTERM

stty -echo
while read -r line; do
  echo "$line"
done < /foo/bar

cleanup

When I press Ctrl-C, my terminal is screwed up because the stty -echo setting is still in effect. I have many other scripts where my cleanup function works flawlessly. The only time I seem to have a problem is when I press Ctrl-C while the script is in a read loop. Is there a way to ensure that the cleanup function will get called when Ctrl-C is pressed while the script is inside a read loop? Or am I just missing something obvious here?

Update: There is something else going on in my scripts. I ran the exact script above, and I can't get it to fail the way my other scripts do. I will have to try to distill the broken scripts down to something that I can get to fail, at which point I will update the question.

Update 2: Okay, I figured it out. I was getting an error from stty (which I wasn't seeing because my real cleanup function was also clearing the screen). The error was: stty: standard input: Inappropriate ioctl for device. I looked this up an apparently it was due to calling stty while stdin was redirected from the file /foo/bar. So I changed my trap call to trap "break" SIGINT SIGHUP SIGTERM and it worked.

like image 409
Mike Holt Avatar asked Mar 25 '14 18:03

Mike Holt


1 Answers

It turns out the problem was due to the fact that my cleanup function was calling stty, and stty evidently doesn't like to be called while stdin is being redirected from a file. Thus, when I pressed Ctrl-C while the script was executing the read loop, the cleanup function got called as if I had called it from within the loop:

while read -r line; do
  ...
  cleanup
  ...
done < "$filename"

This, in turn, meant that stty was executed with a redirected stdin, and it died with the error stty: standard input: Inappropriate ioctl for device.

I was able to fix this by changing my trap line:

trap "break" SIGINT SIGHUP SIGTERM

So instead of having it effectively insert a call to cleanup into my loop when I press Ctrl-C, it instead just (effectively) inserts a break into the loop, thus breaking out of the loop and subsequently calling the cleanup function via the line after the loop.

like image 191
Mike Holt Avatar answered Oct 28 '22 13:10

Mike Holt