Is it possible to case arrow keys in a bash script to run a certain set of commands if the up/left arrow keys are pressed, and a certain set if the down/right arrow keys are
Using eMPee584
answer I think I came up a good solution for you.
Its output is much the same as user3229933
answer but will not be triggered by shift keys and will work in most terminals.
It has UP DOWN LEFT RIGHT HOME and END Keys
Press 'q' to quit
Most of this is thanks to eMPee584
you may need to change '-sn1' to '-sN1' if you get an error like illegal option n
.
#!/bin/bash
while read -sn1 key # 1 char (not delimiter), silent
do
read -sn1 -t 0.0001 k1 # This grabs all three symbols
read -sn1 -t 0.0001 k2 # and puts them together
read -sn1 -t 0.0001 k3 # so you can case their entire input.
key+=${k1}${k2}${k3}
case "$key" in
$'\e[A'|$'\e0A') # up arrow
((cur > 1)) && ((cur--))
echo up;;
$'\e[D'|$'\e0D') # left arrow
((cur > 1)) && ((cur--))
echo left;;
$'\e[B'|$'\e0B') # down arrow
((cur < $#-1)) && ((cur++))
echo down;;
$'\e[C'|$'\e0C') # right arrow
((cur < $#-1)) && ((cur++))
echo right;;
$'\e[1~'|$'\e0H'|$'\e[H') # home key:
cur=0
echo home;;
$'\e[4~'|$'\e0F'|$'\e[F') # end key:
((cur=$#-1))
echo end;;
q) # q: quit
echo Bye!
exit;;
esac
done
# This will bind the arrow keys
while true
do
read -r -sn1 t
case $t in
A) echo up ;;
B) echo down ;;
C) echo right ;;
D) echo left ;;
esac
done
You can read arrow keys as well as other keys without any unusual commands; you just need two read
calls instead of one:
escape_char=$(printf "\u1b")
read -rsn1 mode # get 1 character
if [[ $mode == $escape_char ]]; then
read -rsn2 mode # read 2 more chars
fi
case $mode in
'q') echo QUITTING ; exit ;;
'[A') echo UP ;;
'[B') echo DN ;;
'[D') echo LEFT ;;
'[C') echo RIGHT ;;
*) >&2 echo 'ERR bad input'; return ;;
esac
You can use read -n 1
to read one character then use a case
statement to choose an action to take based on the key.
On problem is that arrow keys output more than one character and the sequence (and its length) varies from terminal to terminal.
For example, on the terminal I'm using, the right arrow outputs ^[[C
. You can see what sequence your terminal outputs by pressing Ctrl-V Right Arrow. The same is true for other cursor-control keys such as Page Up and End.
I would recommend, instead, to use single-character keys like <
and >
. Handling them in your script will be much simpler.
read -n 1 key
case "$key" in
'<') go_left;;
'>') go_right;;
esac
As mentioned before, the cursor keys generate three bytes - and keys like home/end even generate four! A solution I saw somewhere was to let the initial one-char read() follow three subsequent one-char reads with a very short timeout. Most common key sequences can be shown like this f.e.:
#!/bin/bash
for term in vt100 linux screen xterm
{ echo "$term:"
infocmp -L1 $term|egrep 'key_(left|right|up|down|home|end)'
}
Also, /etc/inputrc contains some of these with readline mappings.. So, answering original question, here's a snip from that bash menu i'm just hacking away at:
while read -sN1 key # 1 char (not delimiter), silent
do
# catch multi-char special key sequences
read -sN1 -t 0.0001 k1
read -sN1 -t 0.0001 k2
read -sN1 -t 0.0001 k3
key+=${k1}${k2}${k3}
case "$key" in
i|j|$'\e[A'|$'\e0A'|$'\e[D'|$'\e0D') # cursor up, left: previous item
((cur > 1)) && ((cur--));;
k|l|$'\e[B'|$'\e0B'|$'\e[C'|$'\e0C') # cursor down, right: next item
((cur < $#-1)) && ((cur++));;
$'\e[1~'|$'\e0H'|$'\e[H') # home: first item
cur=0;;
$'\e[4~'|$'\e0F'|$'\e[F') # end: last item
((cur=$#-1));;
' ') # space: mark/unmark item
array_contains ${cur} "${sel[@]}" && \
sel=($(array_remove $cur "${sel[@]}")) \
|| sel+=($cur);;
q|'') # q, carriage return: quit
echo "${sel[@]}" && return;;
esac
draw_menu $cur "${#sel[@]}" "${sel[@]}" "$@" >/dev/tty
cursor_up $#
done
To extend JellicleCat's answer:
#!/bin/bash
escape_char=$(printf "\u1b")
read -rsn1 mode # get 1 character
if [[ $mode == $escape_char ]]; then
read -rsn4 -t 0.001 mode # read 2 more chars
fi
case $mode in
'') echo escape ;;
'[a') echo UP ;;
'[b') echo DOWN ;;
'[d') echo LEFT ;;
'[c') echo RIGHT ;;
'[A') echo up ;;
'[B') echo down ;;
'[D') echo left ;;
'[C') echo right ;;
'[2~') echo insert ;;
'[7~') echo home ;;
'[7$') echo HOME ;;
'[8~') echo end ;;
'[8$') echo END ;;
'[3~') echo delete ;;
'[3$') echo DELETE ;;
'[11~') echo F1 ;;
'[12~') echo F2 ;;
'[13~') echo F3 ;;
'[14~') echo F4 ;;
'[15~') echo F5 ;;
'[16~') echo Fx ;;
'[17~') echo F6 ;;
'[18~') echo F7 ;;
'[19~') echo F8 ;;
'[20~') echo F9 ;;
'[21~') echo F10 ;;
'[22~') echo Fy ;;
'[23~') echo F11 ;;
'[24~') echo F12 ;;
'') echo backspace ;;
*) echo $mode;;
esac