Search code examples
bashwait

Bash read and wait even after input


In bash I want to read a key input from user and wait even after key was pressed. I use

read -n 1 -s -t 0.05 key

but after input it stops waiting. I want to wait 0.05 no matter what and store first key pressed in variable key.


Solution

  • As pointed out in the comments, use sleep. Everything entered during sleep will be printed on the terminal (as for every command that doesn't consume stdin). Use stty to hide this output.

    • stty -echo disables output.
    • stty echo enables output (unconsumed characters hidden so far will be printed).

    You probably want to wait only the remaining time and not an additional 0.05 seconds. There are two ways to do so:

    The easy but possibly unreliable way

    Use sleep before read — that way you neither have to measure the wait time nor have to compute the remaining time:

    stty -echo
    sleep 0.049
    read -n1 -s -t0.001 key;
    stty echo;
    
    echo "key was $key"
    

    The timeout read -t… must be long enough to actually read the buffered characters. You can test this by copy-pasting a very long line into your terminal – read will only read a part of it. On a slow system you may have to increase 0.001. But keep in mind that the combined waiting time for the user can be just the sleep time, that is, a bigger -t means less precision.

    The complicated but robust way

    To circumvent that problem you can put sleep after read and compute the remaining waiting time, which of course is more work. Here is a solution in bash 5.0 or higher:

    # all variables of the form tSomething are in μs = 10^-6 s
    stty -echo
    tStart="${EPOCHREALTIME/./}" 
    read -n1 -s -t0.05 key;
    tEnd="${EPOCHREALTIME/./}"
    printf -v tWait %07d "$((tStart + 50000 - tEnd))" # 50000 μs = 0.05 s
    sleep "${tWait:: -6}.${tWait: -6}"
    stty echo
    
    echo "key was $key"