Search code examples
bashshellgrepshposix

Support for Ctrl+C / Ctrl+Z at " answer=$( while ! head -c 1 | grep -i '[ny]' ; do true ; done ) "


To get a user's [Y/N] decision I am using a "yesno" function, which contains a loop:

answer=$( while ! head -c 1 | grep -i '[ny]' ; do true ; done )

However, this loop is blocking a console and impossible to terminate by pressing Ctrl + C or Ctrl + Z . Adding stuff like [\x03] (ascii for Ctrl + C ) or [\x1A] (ascii for Ctrl + Z ) does not help either. How to add this feature, while preserving the POSIX compatibility and without relying on other tools?

#!/bin/sh

yesno () {
    printf "$1 [Y/N] "
    old_stty_cfg=$( stty -g )
    stty raw -echo
    answer=$( while ! head -c 1 | grep -i '[ny]' ; do true ; done )
    stty "$old_stty_cfg"
    if printf "$answer" | grep -iq "^y" ; then
        return 0
    else
        return 1
    fi
}

if yesno "Question?" ; then
    printf "yes\n"
    return 0
else
    printf "no\n"
    return 1
fi

Solution

  • Comments about this solution and its' POSIX compatibility - will be really appreciated !

    #!/bin/sh
    enter=$( printf '\015' )
    ctrl_c=$( printf '\003' )
    ctrl_x=$( printf '\030' )
    ctrl_z=$( printf '\032' )
    
    yesno () {
        printf '%b [Y/N] ' "$1"
        old_stty_cfg=$( stty -g )
        stty raw -echo
        while true ; do
            answer=$( head -c 1 )
            case $answer in *"$ctrl_c"*|"$ctrl_x"*|"$ctrl_z"*)
                stty "$old_stty_cfg"
                exit 1
                ;;
                *"y"*|"Y"*)
                stty "$old_stty_cfg"
                return 0
                ;;
                *"n"*|"N"*)
                stty "$old_stty_cfg"
                return 1
                ;;
            esac
        done
    }
    
    if yesno "Question?" ; then
        printf "yes\n"
        exit 0
    else
        printf "no\n"
        exit 1
    fi