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
None of the above answers have worked for me! I had to grab bits and pieces from many answers in this thread, as well as other searches via google. It took me about an hour to concoct this.
I am running Ubuntu 20.04 LTS and this works for me (although, it may not be perfect, as I had to 'hack' at it):
waitkey() {
local end=""
local key=""
echo
echo " Press ESC ... "
while [ "$end" == "" ]; do
read -rsn1 key
case "$key" in
$'\x1b')
local k=""
# I'm not sure why I have to do this if statement,
# but without it, there are errors. took forever
# to figure out why 'read' would dump me outta the script
if [ "$IFS" ]; then
read -rsn1 -t 0.1 holder && k="$holder"
else
IFS=read -rsn1 -t 0.1 holder && k="$holder"
fi
if [ "$k" == "[" ]; then
read -rsn1 -t 0.1 holder && kk="$holder"
##############################
# you put your arrow code here
#
# eg:
# case "$kk" in
# "A") echo "up arrow!" ;; # do something ...
# esac
##############################
elif [ "$k" == "O" ]; then
read -rsn1 -t 0.1 holder && kk="$holder"
# I am honestly not knowing what this is for
elif [ "$k" == "" ]; then
end=1
fi
esac
done
}
Not sure if this answer the question directly, but I think it's related - I was wandering where do those codes come from, and I finally found:
It's a bit difficult to read at first; for left arrow, lookup "LEFT 4" in the "Key" column, and for the sequence that bash
sees, look up the 5th ("keymap" - "normal") column, where it is written as "[D 1b 5b 44" - which are the three bytes (27, 91, 68) representing this key.
Finding the thread How to read arrow keys on really old bash? - The UNIX and Linux Forums, inspired me to write a short one-liner which dumps the key codes of keys pressed. Basically, you press a key, then Enter (to trigger ending of read
), and then use hexdump
to output what read
has saved (and finally hit Ctrl-C to exit the loop):
$ while true; do read -p?; echo -n $REPLY | hexdump -C; done
?^[[D
00000000 1b 5b 44 |.[D| # left arrow
00000003
?^[[C
00000000 1b 5b 43 |.[C| # right arrow
00000003
?^[[1;2D
00000000 1b 5b 31 3b 32 44 |.[1;2D| # Shift+left arrow
00000006
?^[[1;2C
00000000 1b 5b 31 3b 32 43 |.[1;2C| # Shift+right arrow
00000006
?^C
So, while arrow keys require 3 bytes - Shift+arrow keys require 6! However, seemingly all these sequence start with 0x1b (27), so one could possibly check for this value for read -n1
, before reading any more bytes; also 5b
remains a second byte in multi-byte sequence for the "normal" and "shift/NUM-Lock" columns of the table above.
Edit: much easier and proper way to scan for terminal codes of pressed keys in Linux is via showkey:
$ showkey
Couldn't get a file descriptor referring to the console
$ showkey -h
showkey version 1.15
usage: showkey [options...]
valid options are:
-h --help display this help text
-a --ascii display the decimal/octal/hex values of the keys
-s --scancodes display only the raw scan-codes
-k --keycodes display only the interpreted keycodes (default)
$ sudo showkey -a
Press any keys - Ctrl-D will terminate this program
^[[A 27 0033 0x1b
91 0133 0x5b
65 0101 0x41
^[[B 27 0033 0x1b
91 0133 0x5b
66 0102 0x42
^[[A 27 0033 0x1b
91 0133 0x5b
65 0101 0x41
^[[D 27 0033 0x1b
91 0133 0x5b
68 0104 0x44
^[[C 27 0033 0x1b
91 0133 0x5b
67 0103 0x43
^C 3 0003 0x03
^M 13 0015 0x0d
^D 4 0004 0x04