Search code examples
bashshellunixkeypress

Wait for a Key press on a Shell Script


I made a Bourne shell script, and I need to improve it by adding a "Press Esc button to execute a command".

This is a working example in BASH :

#!/bin/bash
read -s -n1 key
case $key in
$'\e') echo "escape pressed";;
*) echo "something else" ;;
esac

But I couldn't make it work in Bourne shell — Error : "read: Illegal option -s"

Can you please help me find a Bourne shell solution because almost all the information on Google is about Bash statements.


Solution

  • According to our exchanges in comments, your specific question, and the question on Unix & Linux Stack Exchange Can I read a single character from stdin in POSIX shell?, this is a complete solution:

    #!/bin/sh
    
    set -eu
    
    # usage: readc <variable-name>
    readc()
    {
        if [ -t 0 ]
        then
            # if stdin is a tty device, put it out of icanon, set min and
            # time to sane value, but don't otherwise touch other input or
            # or local settings (echo, isig, icrnl...). Take a backup of the
            # previous settings beforehand.
            saved_tty_settings=$(stty -g)
            stty -echo -icanon min 1 time 0
        fi
        eval "$1="
        while
            # read one byte, using a work around for the fact that command
            # substitution strips trailing newline characters.
            c=$(dd bs=1 count=1 2> /dev/null; echo .)
            c=${c%.}
    
            # break out of the loop on empty input (eof) or if a full character
            # has been accumulated in the output variable (using "wc -m" to count
            # the number of characters).
            [ -n "$c" ] &&
                eval "$1=\${$1}"'$c
            [ "$(($(printf %s "${'"$1"'}" | wc -m)))" -eq 0 ]'; do
            continue
        done
        if [ -t 0 ]
        then
            # restore settings saved earlier if stdin is a tty device.
            stty "$saved_tty_settings"
        fi
    }
    
    # Reads one character.
    readc key
    
    # Acts according to what has been pressed.
    case "$key" in
      "$(printf '%b' '\033')") echo "escape pressed";;
      *) echo "something else" ;;
    esac