shell script respond to keypress

后端 未结 5 1576
逝去的感伤
逝去的感伤 2020-12-06 00:25

I have a shell script that essentially says something like

while true; do
    read -r input
    if [\"$input\" = \"a\"]; then 
        echo \"hello world\"          


        
相关标签:
5条回答
  • 2020-12-06 00:49

    Another way of doing it, in a non blocking way(not sure if its what you want). You can use stty to set the min read time to 0.(bit dangerous if stty sane is not used after)

    stty -icanon time 0 min 0
    

    Then just run your loop like normal. No need for -r.

    while true; do
        read input
    
        if ["$input" = "a"]; then 
            echo "hello world"           
        fi
    done
    

    IMPORTANT! After you have finished with non blocking you must remember to set stty back to normal using

    stty sane
    

    If you dont you will not be able to see anything on the terminal and it will appear to hang.

    You will probably want to inlcude a trap for ctrl-C as if the script is quit before you revert stty back to normal you will not be able to see anything you type and it will appear the terminal has frozen.

    trap control_c SIGINT
    
    control_c()
    {
        stty sane
    }
    

    P.S Also you may want to put a sleep statement in your script so you dont use up all your CPU as this will just continuously run as fast as it can.

    sleep 0.1
    

    P.S.S It appears that the hanging issue was only when i had used -echo as i used to so is probably not needed. Im going to leave it in the answer though as it is still good to reset stty to its default to avoid future problems. You can use -echo if you dont want what you have typed to appear on screen.

    0 讨论(0)
  • 2020-12-06 00:49

    I have a way to do this in my project: https://sourceforge.net/p/playshell/code/ci/master/tree/source/keys.sh

    It reads a single key everytime key_readonce is called. For special keys, a special parsing loop would run to also be able to parse them.

    This is the crucial part of it:

    if read -rn 1 -d '' "${T[@]}" "${S[@]}" K; then
        KEY[0]=$K
    
        if [[ $K == $'\e' ]]; then
            if [[ BASH_VERSINFO -ge 4 ]]; then
                T=(-t 0.05)
            else
                T=(-t 1)
            fi
    
            if read -rn 1 -d '' "${T[@]}" "${S[@]}" K; then
                case "$K" in
                \[)
                    KEY[1]=$K
    
                    local -i I=2
    
                    while
                        read -rn 1 -d '' "${T[@]}" "${S[@]}" "KEY[$I]" && \
                        [[ ${KEY[I]} != [[:upper:]~] ]]
                    do
                        (( ++I ))
                    done
                    ;;
                O)
                    KEY[1]=$K
                    read -rn 1 -d '' "${T[@]}" 'KEY[2]'
                    ;;
                [[:print:]]|$'\t'|$'\e')
                    KEY[1]=$K
                    ;;
                *)
                    __V1=$K
                    ;;
                esac
            fi
        fi
    
        utils_implode KEY __V0
    
    0 讨论(0)
  • 2020-12-06 00:50

    so the final working snippet is the following:

    #!/bin/bash
    
    while true; do
    read -rsn1 input
    if [ "$input" = "a" ]; then
        echo "hello world"
    fi
    done
    
    0 讨论(0)
  • 2020-12-06 00:51
    read -rsn1
    

    Expect only one letter (and don't wait for submitting) and be silent (don't write that letter back).

    0 讨论(0)
  • 2020-12-06 00:55

    You can use this getkey function:

    getkey() {
        old_tty_settings=$(stty -g)   # Save old settings.
        stty -icanon
        Keypress=$(head -c1)
        stty "$old_tty_settings"      # Restore old settings.
    }
    

    It temporarily turns off "canonical mode" in the terminal settings (stty -icanon) then returns the input of "head" (a shell built-in) with the -c1 option which is returning ONE byte of standard input. If you don't include the "stty -icanon" then the script echoes the letter of the key pressed and then waits for RETURN (not what we want). Both "head" and "stty" are shell built-in commands. It is important to save and restore the old terminal settings after the key-press is received.

    Then getkey() can be used in combination with a "case / esac" statement for interactive one-key selection from a list of entries: example:

    case $Keypress in
       [Rr]*) Command response for "r" key ;;
       [Ww]*) Command response for "w" key ;;
       [Qq]*) Quit or escape command ;;  
    esac
    

    This getkey()/case-esac combination can be used to make many shell scripts interactive. I hope this helps.

    0 讨论(0)
提交回复
热议问题