0

I've added the following to the top of my bash script:

quit() {

echo "Do you want to quit ? (y/n)"
  read n
  if [ "$n" = 'y' ]; then
    exit
  fi

}

trap quit INT
trap quit SIGINT
trap quit SIGTERM

The script asks the user a series of questions and then performs actions based on the results. Pressing CTRL+C seems to work some of the time.

But other times I just get Do you want to quit ? (y/n) and the script locks up. This may happen when in an IF statement or within WHILE / DONE.

But it seems to be if you time the CTRL+C when an echo happens I can make the issue happen.. ie: CTRL+C at the same time as the echo.

Is there a way to always trap CTRL+C and prompt the user ? then let them decide if they want to quit or not ?

This code shows the issue happening..

quit() {

echo "Do you want to quit ? (y/n)"
  read n
  if [ "$n" = 'y' ]; then
    exit
  fi

}

trap quit INT
trap quit SIGINT
trap quit SIGTERM

for i in `seq 1 50`; do
    sleep 1
    echo -e "........"
    read -i "0000" -e site
done

Try to time the CTRL+C just as the '........' and 0000 show on screen and the issue happens.

This seems to have made a big difference.

quit() {

    while read -e -t 0.1; do : ; done
    read  -p "Do you want to quit ? (y/n) " n
    if [ "$n" = 'y' ]; then
        exit
    fi

}

I'm struggling to make it happen now.

Thanks

10 Rep
  • 2,217
  • 7
  • 19
  • 33
Tom
  • 1,436
  • 24
  • 50
  • 2
    My guess is that you have something in stdin that is read into `$n` and since it's different from `y` your script just carries on after executing the trap function. Does that sound possible? If so I suggest checking [this Unix&Linux SE question](https://superuser.com/questions/276531/clear-stdin-before-reading) to discard the content of stdin before prompting for y/n – Aaron Jul 08 '19 at 16:24
  • Not related to your question, but the `-e` argument to `echo` is best avoided; it's not specified by the POSIX sh standard, and even in bash, its behavior varies wildly depending on the active compile-time and runtime flags. See Stephane's excellent answer in https://unix.stackexchange.com/questions/65803/why-is-printf-better-than-echo describing the respects in which `echo`'s behavior is unreliable and unportable, *especially* when relying on backslash-escape translation. – Charles Duffy Jul 08 '19 at 16:29
  • 1
    I'm unfamiliar with `read`'s `-i` flag but given its description I think it's the offender : "If readline is being used to read the line, text is placed into the editing buffer before editing begins." ; it must be placing `0000` into `stdin`, which your `quit()` function reads instead of the `y/n` you'd like – Aaron Jul 08 '19 at 16:30
  • I think it is the `read -i` - is there anyway around this ? I've followed the link @Aaron provided but i'm not sure how to add that in.. thanks – Tom Jul 08 '19 at 16:43
  • @Aaron I don't think it's continuing .. It appears to be hing when this happens. – Tom Jul 08 '19 at 16:47
  • Updated question with possible solution.. – Tom Jul 08 '19 at 16:54
  • I would guess it just seems to hang because it's waiting for another line of input for the main flow's `read`. If you decide to use the solution from the question I linked to, you should empty stdin inside your trap before reading `n`. – Aaron Jul 08 '19 at 17:18
  • Or `cat /dev/tty | read -r n`, so it does not mess with `stdin` but reads the answer from the terminal device. In this case you will have to determine if the script is running interactive before using such interactive handler. – Léa Gris Jul 08 '19 at 17:28
  • Only issue I've found with method I've added to the question, is if you press 'n' then the new line you get doesn't have your previous prefilled answer. – Tom Jul 09 '19 at 07:14

2 Answers2

0

Trap names must not be prefixed with SIG as it is undefined.

The other thing you want to do inside your trap handler is: neutralize the traps so they don't trigger while handler is processing.

#!/usr/bin/env sh

quit() {
  trap false ABRT INT # Neutralize traps in the quit handler by assigning the false function
  printf '\nDo you want to quit ? (y/N)\n' # Uppercase N show the default choice
  read -r n
  case "${n}" in
    [yY]) # Match y or Y
      exit
    ;;
  esac
  trap quit ABRT INT # Re-enable traps on self
}

trap quit ABRT INT # Attach the SIGABRT and SIGINT traps to the quit function
i=1
while true; do
  echo "$i"
  i=$((i+1))
  sleep 1
done

Sample run:

1
2
^C
Do you want to quit ? (y/n)
n
3
4
^C
Do you want to quit ? (y/n)
^C5
6
7
8
^C
Do you want to quit ? (y/n)
y
Léa Gris
  • 17,497
  • 4
  • 32
  • 41
  • I added a couple more comments – Léa Gris Jul 08 '19 at 17:08
  • This suffers the same issue. If you press CTRL+C just at the 'right' the script appears to hang and won't accept any input. The update on my question seems to work better. – Tom Jul 09 '19 at 07:12
0

Try the following.

quit() {

  echo "Do you want to quit ? (y/n)"
  read n
  if [ "$n" = 'y' ]; then
    exit
  fi

}

trap quit INT
trap quit SIGINT
trap quit SIGTERM

for i in `seq 1 50`; do
    sleep 1
    echo -e "........"
    site=$(read -p "0000" -e site; echo "$site")
done
David Anderson
  • 1,108
  • 9
  • 17