How to read with timeout without making bash consider it an error?
By Joseph Russell •
I have a read_user_input.sh script:
#!/bin/bash
# set -e
prompt="bla? [Y/n] "
while true; do read -p "$prompt" -n 1 -s -t 3 reply case $reply in ""|Y|y) echo "bla!!!"; break;; N|n) echo "no bla :( you suck"; break;; *) ;; esac
doneWorks as I expected - i.e.
- User inputs "Y" -> bla
- User inputs "y" -> bla
- User hits Enter -> bla
- Timeout -> bla
However, when adding set -e - the read ends with error > 128.
From read --help:
Exit Status:The return code is zero, unless end-of-file is encountered, read times out (in which case it's greater than 128), a variable assignment error occurs, or an invalid file descriptor is supplied as the argument to -u.
What would be the best way to overcome this?
- adding
|| trueseems not right, as it would hide any real errors. - I also don't want to remove the
set -e. - One other thing I thought about was handling it in
trapbut that seems like an overkill
3 Answers
Instead of adding || true, set the $reply to y if there was a timeout:
read -p "$prompt" -n 1 -s -t 3 reply || { err=$? if (( $err > 128 )) ; then reply=y else exit $err fi } 6 Here is a hack where is used command substitution which forks a subshell where -e is not enabled.
#!/bin/bash
set -e
prompt="bla? [Y/n] "
while true; do case $(read -p "$prompt" -n 1 -s -t 3 reply; echo $reply) in ""|Y|y) echo "bla!!!"; break;; N|n) echo "no bla :( you suck"; break;; *) ;; esac
done 1 Using @choroba answer, I changed my switch case into:
#!/bin/bash
set -e
prompt="bla? [Y/n] "
while true; do read -p "$prompt" -n 1 -s -t 3 reply || (( $? > 128 )) case $reply in ""|Y|y) echo "bla!!!"; break;; N|n) echo "no bla :( you suck"; break;; *) ;; esac
done