Pure bash: unattended user input over loop
I've done this without having to play with stty:
loop=true loopDelay=.05
while $loop; do
trapKey=
if IFS= read -d '' -rsn 1 -t $loopDelay str; then
while IFS= read -d '' -rsn 1 -t .002 chr; do
str+="$chr"
done
case $str in
$'\E[A') trapKey="<UP>" ;;
$'\E[B') trapKey="<DOWN>" ;;
$'\E[C') trapKey="<RIGHT>" ;;
$'\E[D') trapKey="<LEFT>" ;;
q | $'\E') loop=false;echo ;;
* ) trapKey=${str@Q} ;;
esac
fi
if [ "$trapKey" ] ;then
printf "\nDoing something with %s.\n" "$trapKey"
fi
echo -n .
done
This will
- loop with a very small footprint (max 2 millisecond)
- react to keys cursor left, cursor right, cursor up and cursor down
- exit loop with key Escape or q.
Explanation:
As keyboard don't retun character but key pressed, some key could send more than one character, like Home wich should send an escape sequence: \e[H
. there are 3 characters.
For supporting this I build a loop over a very small timeouted read
command:
if IFS= read -d '' -rsn 1 -t $LOOPDELAY str; then
while IFS= read -d '' -rsn 1 -t .002 chr; do
str+="$chr"
done
If no character read after $LOOPDELAY
then no keyboard key is read. Else $str
variable will be completed by all character read
could acces in less than 0.002
seconds.
Nicer version with support of Fx
keys:
#!/bin/bash
loopDelay=.042
# printf -v shapes "%b " \\U28{01,08,10,20,80,40,04,02}
printf -v shapes "%b " \\U28{19,38,B0,e0,c4,46,07,0b}
shapes=($shapes)
declare -A csiKeys='( [15~]=F5 [17~]=F6 [18~]=F7 [19~]=F8 [20~]=F9 [21~]=F10
[23~]=F11 [24~]=F12 [A]=UP [B]=DOWN [C]=RIGHT [D]=LEFT [H]=HOME [F]=END
[2~]=INSERT [3~]=DELETE [5~]=PGUP [6~]=PGDOWN )' \
escKeys='( [OP]=F1 [OQ]=F2 [OR]=F3 [OS]=F4 )'
loop=true
while $loop; do
trapKey=
if IFS= read -d '' -rsn 1 -t $loopDelay str; then
while IFS= read -d '' -rsn 1 -t .002 chr; do str+="$chr" ; done
if [[ ${str::2} == $'\e[' ]] && [[ -v "csiKeys['${str:2}']" ]] ;then
trapKey="${csiKeys[${str:2}]}"
elif [[ ${str::1} == $'\e' ]] && [[ -v "escKeys['${str:1}']" ]] ;then
trapKey="${escKeys[${str:1}]}"
elif [[ ${str/$'\e'/q} == q ]];then
printf '"%q" pressed, exit.\n' "$str"
loop=false
else
trapKey=${str@Q}
fi
fi
if [ "$trapKey" ] ;then
printf "Doing something with %s.\n" "$trapKey"
fi
printf >&2 '%s\r' ${shapes[shcnt++%8]}
done