iamhungry129
iamhungry129

Reputation: 166

Trapping ctrl+c in a bash script after a read in silent mode

The script I'm working on reads in a single character in silent mode and stores it to $c

Heres a tester script which does the same thing as an example:

    #!/bin/bash

    gracefulExit() {
          echo "exiting..."
          #other stuff would go here
          exit 1
    }

    trap gracefulExit INT

    while [ 1 = 1 ]
    do
          read -s -r -n1 c
          echo "character read"
    done

The problem I'm having is that if you use ctrl+c to break out of the script, the shell stays in silent mode (I can't see anything I type) and it won't return to normal until I exit. I noticed this only happened after I was trapping ctrl+c (without the trap, its kicked from silent mode). My script requires me to trap so I can clean up.

This is on 4.2.10

Any ideas?

Thank you very much for your help!

Upvotes: 6

Views: 1431

Answers (2)

tony994
tony994

Reputation: 515

Try to reenable the tty echo in the exit function:

#!/bin/bash

gracefulExit() {
      echo "exiting..."
      # reenable tty echo
      stty icanon echo echok
      exit 1
}

trap gracefulExit INT

while [ 1 = 1 ]
do
      read -s -r -n1 c
      echo "character read"
done

In my test on OS X read disabled these settings: icanon echo echok. You can check that for your system with stty -a

Run it before you call your script and then again after your script finished and check the difference in the output to identify what you program changed. Then set the changed flags back.

$ diff before.out after.out
2,3c2,3
< lflags: icanon isig iexten echo echoe echok echoke -echonl echoctl
<   -echoprt -altwerase -noflsh -tostop -flusho pendin -nokerninfo
---
> lflags: -icanon isig iexten -echo echoe -echok echoke -echonl echoctl
>   -echoprt -altwerase -noflsh -tostop -flusho -pendin -nokerninfo

here you can see the changed flags: icanon, echo, echok got disabled. You can identify that with the "-" at the beginning.

and finally here's another version of your script that does the full job automated:

#!/bin/bash

# save the current tty flags
tty_flags=`stty -g`

gracefulExit() {
      echo "exiting..."
      # set the saved tty flags
      stty $tty_flags
      exit 1
}

trap gracefulExit INT

while [ 1 = 1 ]
do
      read -s -r -n1 c
      echo "character read"
done

Upvotes: 2

mikeserv
mikeserv

Reputation: 694

You could use DD, I guess, though I don't understand what you're trying to change exactly.

c=$(stty raw;dd bs=1 count=1 if=$(tty) status=none;stty sane)

That will at least quit reading as soon as stdin you enter a single byte. Just type a key and $c is set.

Upvotes: 0

Related Questions