The script isn't waiting for a key.
That file is consumed by both read
commands (and anything else inside the loop that read stdin).
The correct solution for shell's read
that have the option -u
(and bash does), is to define the fd
(file descriptor) to use in each read while the file is redirected to some fd
number (greater than 2):
while read -u 3 line ; do
while : ; do
read -u 1 -n 1 key
if [[ $key = q ]] ; then
break
fi
done
echo "$line"
done 3< "$1"
That makes the first read get the input from fd 3
which comes from the file (done 3< "$1"
), and the second read get the input from fd 1
(stdin).
For POSIX shells, read does not have the -u
option, we need to perform some redirections to get the same general effect:
#!/bin/dash
while read line <&3; do
while : ; do
read key <&1
if [ "$key" = q ] ; then
break
fi
done
done 3< "$1"
Sadly, this also remove the -n 1
option from read and each key from the keyboard must be followed by pressing Enter.
To actually read one character we may use dd
. And we also may set the actual terminal as /dev/tty
(blocks any other redirection) and if we need to hide the text typed (or passwords) use stty -echo
:
#!/bin/dash
while read line <&3; do
while : ; do
stty raw -echo
key=$(dd bs=1 count=1 </dev/tty 2> /dev/null)
stty -raw echo
if [ "$key" = q ] ; then
break
fi
done
echo "$line"
done 3< "$1"
Caveat: setting stty raw
will prevent the effect of keys like CTRL-C (be careful).