Bash: How to end infinite loop with any key pressed?

前端 未结 6 731
长发绾君心
长发绾君心 2020-11-30 03:16

I need to write an infinite loop that stops when any key is pressed.

Unfortunately this one loops only when a key is pressed.

Ideas please?

#         


        
相关标签:
6条回答
  • 2020-11-30 03:51

    I found this forum post and rewrote era's post into this pretty general use format:

    # stuff before main function
    printf "INIT\n\n"; sleep 2
    
    INIT(){
      starting="MAIN loop starting"; ending="MAIN loop success"
      runMAIN=1; i=1; echo "0"
    }; INIT
    
    # exit script when MAIN is done, if ever (in this case counting out 4 seconds)
    exitScript(){
        trap - SIGINT SIGTERM SIGTERM # clear the trap
        kill -- -$$ # Send SIGTERM to child/sub processes
        kill $( jobs -p ) # kill any remaining processes
    }; trap exitScript SIGINT SIGTERM # set trap
    
    MAIN(){
      echo "$starting"
      sleep 1
    
      echo "$i"; let "i++"
      if (($i > 4)); then printf "\nexiting\n"; exitScript; fi
    
      echo "$ending"; echo
    }
    
    # main loop running in subshell due to the '&'' after 'done'
    { while ((runMAIN)); do
      if ! MAIN; then runMain=0; fi
    done; } &
    
    # --------------------------------------------------
    tput smso
    # echo "Press any key to return \c"
    tput rmso
    oldstty=`stty -g`
    stty -icanon -echo min 1 time 0
    dd bs=1 count=1 >/dev/null 2>&1
    stty "$oldstty"
    # --------------------------------------------------
    
    # everything after this point will occur after user inputs any key
    printf "\nYou pressed a key!\n\nGoodbye!\n"
    

    Run this script

    0 讨论(0)
  • 2020-11-30 03:54

    read has a number of characters parameter -n and a timeout parameter -t which could be used.

    From bash manual:

    -n nchars read returns after reading nchars characters rather than waiting for a complete line of input, but honors a delimiter if fewer than nchars characters are read before the delimiter.

    -t timeout

    Cause read to time out and return failure if a complete line of input (or a specified number of characters) is not read within timeout seconds. timeout may be a decimal number with a fractional portion following the decimal point. This option is only effective if read is reading input from a terminal, pipe, or other special file; it has no effect when reading from regular files. If read times out, read saves any partial input read into the specified variable name. If timeout is 0, read returns immediately, without trying to read any data. The exit status is 0 if input is available on the specified file descriptor, non-zero otherwise. The exit status is greater than 128 if the timeout is exceeded.

    However, the read builtin uses the terminal which has its own settings. So as other answers have pointed out we need to set the flags for the terminal using stty.

    #!/bin/bash
    old_tty=$(stty --save)
    
    # Minimum required changes to terminal.  Add -echo to avoid output to screen.
    stty -icanon min 0;
    
    while true ; do
        if read -t 0; then # Input ready
            read -n 1 char
            echo -e "\nRead: ${char}\n"
            break
        else # No input
            echo -n '.'
            sleep 1
        fi       
    done
    
    stty $old_tty
    
    0 讨论(0)
  • 2020-11-30 03:55

    Here is another solution. It works for any key pressed, including space, enter, arrows, etc.

    The original solution tested in bash:

    IFS=''
    if [ -t 0 ]; then stty -echo -icanon raw time 0 min 0; fi
    while [ -z "$key" ]; do
        read key
    done
    if [ -t 0 ]; then stty sane; fi
    

    An improved solution tested in bash and dash:

    if [ -t 0 ]; then
       old_tty=$(stty --save)
       stty raw -echo min 0
    fi
    while
       IFS= read -r REPLY
       [ -z "$REPLY" ]
    do :; done
    if [ -t 0 ]; then stty "$old_tty"; fi
    

    In bash you could even leave out REPLY variable for the read command, because it is the default variable there.

    0 讨论(0)
  • 2020-11-30 04:00

    Usually I don't mind breaking a bash infinite loop with a simple CTRL-C. This is the traditional way for terminating a tail -f for instance.

    0 讨论(0)
  • 2020-11-30 04:02

    You need to put the standard input in non-blocking mode. Here is an example that works:

    #!/bin/bash
    
    if [ -t 0 ]; then
      SAVED_STTY="`stty --save`"
      stty -echo -icanon -icrnl time 0 min 0
    fi
    
    count=0
    keypress=''
    while [ "x$keypress" = "x" ]; do
      let count+=1
      echo -ne $count'\r'
      keypress="`cat -v`"
    done
    
    if [ -t 0 ]; then stty "$SAVED_STTY"; fi
    
    echo "You pressed '$keypress' after $count loop iterations"
    echo "Thanks for using this script."
    exit 0
    

    Edit 2014/12/09: Add the -icrnl flag to stty to properly catch the Return key, use cat -v instead of read in order to catch Space.

    It is possible that cat reads more than one character if it is fed data fast enough; if not the desired behaviour, replace cat -v with dd bs=1 count=1 status=none | cat -v.

    Edit 2019/09/05: Use stty --save to restore the TTY settings.

    0 讨论(0)
  • 2020-11-30 04:07

    Pure bash: unattended user input over loop

    I've done this without having to play with stty:

    loop=true
    while $loop; do
        trapKey=
        if IFS= read -d '' -rsn 1 -t .002 str; then
            while IFS= read -d '' -rsn 1 -t .002 chr; do
                str+="$chr"
            done
            case $str in
                $'\E[A') trapKey=UP    ;;
                $'\E[B') trapKey=DOWN  ;;
                $'\E[C') trapKey=RIGHT ;;
                $'\E[D') trapKey=LEFT  ;;
                q | $'\E') loop=false  ;;
            esac
        fi
        if [ "$trapKey" ] ;then
            printf "\nDoing something with '%s'.\n" $trapKey
        fi
        echo -n .
    done
    

    This will

    • loop with a very small footprint (max 2 millisecond)
    • react to keys cursor left, cursor right, cursor up and cursor down
    • exit loop with key Escape or q.
    0 讨论(0)
提交回复
热议问题