So, I want to write a bash script that are a sequence of steps and ill identify it as \"task#\". However, each step is only completed and can run as long as the user wants.
kev's great solution works well even in Bash 3.x., but it introduces a 1-second delay (-t 1
) in every loop iteration.
In Bash 3.x, the lowest supported value for -t
(timeout) is 1
(second), unfortunately.
Bash 4.x supports 0
and fractional values, however:
A solution that supports an arbitrary key such as q
requires a nonzero -t
value, but you can specify a value very close to 0
to minimize the delay:
#!/bin/bash
# !! BASH 4.x+ ONLY
while :; do
# Loop command
date
# Check for 'q' keypress *waiting very briefly* and exit the loop, if found.
read -t 0.01 -r -s -N 1 && [[ $REPLY == 'q' ]] && break
done
# Post-loop command
date +%s
Caveat: The above uses 0.01
as the almost-no-timeout value, but, depending on your host platform, terminal program and possibly CPU speed/configuration, a larger value may be required / a smaller value may be supported. If the value is too small, you'll see intermittent error setting terminal attributes: Interrupted system call
errors - if anyone knows why, do tell us.
Tip of the hat to jarno for his help with the following:
Using -t 0
, works as follows, according to help read
(emphasis added):
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.
As of Bash v4.4.12 and 5.0.11, unfortunately, -t 0
seems to ignore -n
/ -N
, so that only an ENTER keypress (or a sequence of keypresses ending in ENTER) causes read
to indicate that data is available.
If anyone knows whether this is a bug or whether there's a good reason for this behavior, do let us know.
Therefore, only with ENTER as the quit key is a -t 0
solution currently possible:
#!/bin/bash
# !! BASH 4.x+ ONLY
while :; do
# Loop command
date
# Check for ENTER keypress and, after clearing the input buffer
# with a dummy `read`, exit the loop.
read -t 0 -r -N 1 && { read -r; break; }
done
# Post-loop command
date +%s