bash: non blocking read inside a loop

14,674

Solution 1

From help read:

  -t timeout    time out and return failure if a complete line of input is
        not read within TIMEOUT seconds.  The value of the TMOUT
        variable is the default timeout.  TIMEOUT may be a
        fractional number.  If TIMEOUT is 0, read returns immediately,
        without trying to read any data, returning success only if
        input is available on the specified file descriptor.  The
        exit status is greater than 128 if the timeout is exceeded

So try:

while true
do
    echo "$var"
    IFS= read -r -t 0.5 -n 1 -s holder && var="$holder"
done

The holder variable is used since a variable loses its contents when used with read unless it is readonly (in which case it's not useful anyway), even if read timed out:

$ declare -r a    
$ read -t 0.5 a
bash: a: readonly variable
code 1

I couldn't find any way to prevent this.

Solution 2

A bit late but a (better) solution could be:

while true ; do
    read -r -s -t 0.5; RETVAL=$?
    # ok? echo && continue
    [ $RETVAL -eq 0 ] && echo -E "$REPLY" && continue
    # no timeout ? (EOF or error) break
    [ $RETVAL -gt 128 ] || break
done

IMHO a bigger timeout won't hurt anybody, as "read" returns as soon as a new line is available...

Share:
14,674

Related videos on Youtube

Artak Begnazaryan
Author by

Artak Begnazaryan

Updated on September 18, 2022

Comments

  • Artak Begnazaryan
    Artak Begnazaryan over 1 year

    I have a following small bash script

    var=a
    
    while true :
    do
        echo $var
        sleep 0.5
        read -n 1 -s var
    done
    

    It just prints the character entered by the user and waits for the next input. What I want to do is actually not block on the read, i.e. every 0.5 second print the last character entered by the user. When user presses some key then it should continue to print the new key infinitely until the next key press and so on.

    Any suggestions?

    • Andra
      Andra almost 8 years
      I know is not an answer to the question, but i had a similar issue, where I needed a values coming through STDIN stored in a variable, but read was halting my program and as mentioned if read times out if will empty the content of the variable, my solution was to use parameter expansion, what it does is, if it times out if assigns a "default" value to the variable... var=a while true do echo $var read -t 0.1 input input=${input:-$var} var=$input sleep 0.1 done as I said is not the right answer for you as you just wanted the key pressed and not the value entered... NOTE: whit this timings there
  • Artak Begnazaryan
    Artak Begnazaryan over 9 years
    What I've tried before was using again the -t option but without holder variable and it was not working. Turns out that read doesn't hold the variable value even if there is no input in the given timeout.
  • muru
    muru over 9 years
    @ArtakBegnazaryan Yes, perhaps I should have mentioned that, but I was looking for ways to disable it and lost track.