Bash case with arrow keys (and DEL, BACKSPACE,etc)
ProgrammingThis forum is for all programming questions.
The question does not have to be directly related to Linux and any language is fair game.
Notices
Welcome to LinuxQuestions.org, a friendly and active Linux Community.
You are currently viewing LQ as a guest. By joining our community you will have the ability to post topics, receive our newsletter, use the advanced search, subscribe to threads and access many other special features. Registration is quick, simple and absolutely free. Join our community today!
Note that registered members see fewer ads, and ContentLink is completely disabled once you log in.
If you have any problems with the registration process or your account login, please contact us. If you need to reset your password, click here.
Having a problem logging in? Please visit this page to clear all LQ-related cookies.
Get a virtual cloud desktop with the Linux distro that you want in less than five minutes with Shells! With over 10 pre-installed distros to choose from, the worry-free installation life is here! Whether you are a digital nomad or just looking for flexibility, Shells can put your Linux machine on the device that you want to use.
Exclusive for LQ members, get up to 45% off per month. Click here for more info.
Bash case with arrow keys (and DEL, BACKSPACE,etc)
I am trying to write a bash script using 'case' but I cannot figure out what the escape sequence would be for the arrow keys. Also, getting the escape sequence for the delete and backspace keys would be useful too. I know that \d is supposed to be delete, but it is not detecting properly....
If you don't mind me asking, is there any specific reason why you (are forced to) focus on using those keys?
Sure, I am writing a bash script that grabs a single key, and throws the input to a 'case' select system. It would be nice to be able to assign actions when someone uses the arrow keys, but that is not the real reason I want to know how to accommodate the arrow keys. Currently with my code, either the script crashes or quits (not quite sure) when I use arrow keys, the DEL key. Also I cannot seem to assign control keys like "CTRL+C" to close the script. Without being able to specify an action, the script just keeps running.
Hmm. Similar to OSD stuff, right? To start with the last, CTRL+C can't be assigned since it sends a signal. The "best" ways to handle that within a script is to use "trap" since you can assign anything to that like a function to clean up before you go. Crashing is always interesting :-] Did you try to run your script with "set -xe" below the shebang line and look at the output? As far as the keys thing is concerned there are scripts (here on LQ too IIRC) that read single char input but in what way you can use it, I don't know. The problem is you probably will have to intercept settings (maybe using a custom inputrc) to make the keys emit something you can use. To give you an idea see: http://www.ibb.net/~anne/keyboard/troubleshooting.html.
This is a belated response - but maybe it can help someone else searching for a solution. Here is an example how to capture most keys and map them in the same file.
#!/bin/bash4 #(i copied bashV4 from F11 since it has associative arrays)
while IFS='^B' read -sn1 a # ^B is Control B
do
k=`perl -e "printf('%d',ord('$a'));"`
if (( k == 27 )) ; then
while IFS='' read -sn1 -t1 c
do
l=`perl -e "printf('%d',ord('$c'));"`
k="$k $l"
grep "^$k 0 " $0 > /dev/null
if (( $? == 0 )) ; then
break
fi
done
fi
echo $k
grep "^$k 0 " $0
done
# never gets here
exit
for anybody who like me ended up here looking for this solution.
this solution should manage just about any key combination.
Note alot of the keys may/will be mapped to X, window manager, terminal, etc...
Only real proble is detecting Escape,ctrl-[,ctrl-O as they are part of the sequences of the other keys.
so for example with this implementation pressing Escape 2 within a second will return ctrl-[ instead of Escape.
Last version no, I think unless someone finds a bug in it.
I'd have to disagree. for my purposes c and ncurses would be overkill.
I've wanted this sort of functionality in bash for a while now, so I finally got myself to do it.
And while I was at it I wanted a decent solution.
Anyway now fixed the last of the problems and optimized for performance.
takes less than 0.005s for all keys now. most are less than 0.001s.
Also reduced the footprint.
Another rewrite, as I got more comfortable with how it all works.
Added full mouse support at least as much as seems to be supported by my terminal i.e. btn 1-3 scroll up and scroll down & motion tracking.
added the codes for Sun Function keys and Meta key. i.e. Ctrl Alt Shift & Meta.
Code:
#!/bin/bash
#set -o errexit
#set -o errtrace
set -o nounset
if [ "${S8C1T:-0}" != "1" ] ; then
declare -gr SS3=$'\eO' # Single Shift Select of G3 Character Set ( SS3 is 0x8f): affects next character only
declare -gr CSI=$'\e[' # Control Sequence Introducer ( CSI is 0x9b)
else
declare -gr SS3=$'\x8f' # Single Shift Select of G3 Character Set ( SS3 is 0x8f): affects next character only
declare -gr CSI=$'\x9b' # Control Sequence Introducer ( CSI is 0x9b)
fi
function vt100_DECRST {
IFS=';' eval 'echo -n "${CSI}?${*?Missing Pm}l"'
}
function vt100_DECSET {
IFS=';' eval 'echo -n "${CSI}?${*?Missing Pm}h"'
}
mouse_type=(
[0]=9 ## X10 mouse reporting, for compatibility with X10's xterm, reports on button press.
[1]=1000 ## X11 mouse reporting, reports on button press and release.
[2]=1001 ## highlight reporting, useful for reporting mouse highlights
[3]=1002 ## button movement reporting, reports movement when a button is presse
[4]=1003 ## all movement reporting, reports all movements.
[5]=1004 ## FocusIn/FocusOut can be combined with any of the mouse events since it uses a different protocol. When set, it causes xterm to send CSI I when the terminal gains focus, and CSI O when it loses focus.
[6]=1005 ## Extended mouse mode enables UTF-8 encoding for C x and C y under all tracking modes, expanding the maximum encodable position from 223 to 2015. For positions less than 95, the resulting output is identical under both modes. Under extended mouse mode, positions greater than 95 generate "extra" bytes which will confuse applications which do not treat their input as a UTF-8 stream. Likewise, C b will be UTF-8 encoded, to reduce confusion with wheel mouse events.
)
function ord {
printf -v "${1?Missing Dest Variable}" "${3:-%d}" "'${2?Missing Char}"
}
function ord.eascii {
LC_CTYPE=C ord "${@}"
}
function AdjustMousePos {
local -i _INDEX
ord.eascii _INDEX "${2}"
eval ${1}'=$(( ${_INDEX}-32))'
}
###############################
##
## READ KEY CRAP
##
##
###############################
KeyModifiers=(
[2]="S" [3]="A" [4]="AS" [5]="C" [6]="CS" [7]="CA" [8]="CAS"
[9]="M" [10]="MS" [11]="MA" [12]="MAS" [13]="MC" [14]="MCS" [15]="MCA" [16]="MCAS"
)
KeybFntKeys=(
[1]="home" [2]="insert" [3]="delete" [4]="end" [5]="pageUp" [6]="pageDown"
[11]="f1" [12]="f2" [13]="f3" [14]="f4" [15]="f5"
[17]="f6" [18]="f7" [19]="f8" [20]="f9" [21]="f10"
[23]="f11" [24]="f12" [25]="f13" [26]="f14" [28]="f15"
[29]="f16" [31]="f17" [32]="f18" [33]="f19" [34]="f20"
)
SunKeybFntKeys=(
[214]="home" [2]="insert" [3]="delete" [4]="end" [216]="pageUp" [222]="pageDown"
[224]="f1" [225]="f2" [226]="f3" [227]="f4" [228]="f5"
[229]="f6" [230]="f7" [231]="f8" [232]="f9" [233]="f10"
[192]="f11" [193]="f12" [218]="keypad-five" [220]="keypad-delete"
)
KeybFntKeysAlt=(
# A B C D E F H
[0x41]="up" [0x42]="down" [0x43]="right" [0x44]="left" [0x45]="keypad-five" [0x46]="end" [0x48]="home"
# I O
[0x49]="InFocus" [0x4f]="OutOfFocus"
# P Q R S Z
[0x50]="f1" [0x51]="f2" [0x52]="f3" [0x53]="f4" [0x5a]="S-HT"
)
C0CtrlChars=(
[0x00]="Null" [0x01]="SOH" [0x02]="STX" [0x03]="ETX" [0x04]="EOT" [0x05]="ENQ" [0x06]="ACK"
[0x07]="BEL" [0x08]="BS" [0x09]="HT" [0x0A]="LF" [0x0B]="VT" [0x0C]="FF" [0x0D]="CR"
[0x0E]="SO" [0x0F]="SI" [0x10]="DLE" [0x11]="DC1" [0x12]="DC2" [0x13]="DC3" [0x14]="DC4"
[0x15]="NAK" [0x16]="SYN" [0x17]="ETB" [0x18]="CAN" [0x19]="EM" [0x1A]="SUB" [0x1B]="ESC"
[0x1C]="FS" [0x1D]="GS" [0x1E]="RS" [0x1F]="US" [0x20]="SP" [0x7F]="DEL"
)
C1CtrlCharsAlt=(
[0x01]="CA-A" [0x02]="CA-B" [0x03]="CA-C" [0x04]="CA-D" [0x05]="CA-E" [0x06]="CA-F" [0x07]="CA-G"
[0x08]="CA-H" [0x09]="CA-I" [0x0a]="CA-J" [0x0b]="CA-K" [0x0c]="CA-L" [0x0d]="CA-M" [0x0e]="CA-N"
[0x0f]="CA-O" [0x10]="CA-P" [0x11]="CA-Q" [0x12]="CA-R" [0x13]="CA-S" [0x14]="CA-T" [0x15]="CA-U"
[0x16]="CA-V" [0x17]="CA-W" [0x18]="CA-X" [0x19]="CA-Y" [0x1a]="CA-Z" [0x1b]="CA-[" [0x1c]="CA-]"
[0x1d]="CA-}" [0x1e]="CA-^" [0x1f]="CA-_" [0x20]="CA-SP" [0x7F]="A-DEL"
)
MouseButtons=(
[0x00]="MB1-P" [0x01]="MB2-P" [0x02]="MB3-P" [0x03]="MB-R"
[0x20]="MB1-M" [0x21]="MB2-M" [0x22]="MB3-M" [0x23]="MB-M"
[0x40]="MB4-P" [0x41]="MB5-P"
)
MouseMetaButtons=(
[0x04]="S-" [0x08]="A-" [0x0c]="AS-"
[0x10]="C-" [0x14]="CS-" [0x1c]="CAS-"
)
function GetMouseButton {
local MouseBtn
AdjustMousePos MouseBtn "${2}"
MouseBtn="${MouseMetaButtons[$(( ${MouseBtn} & 0x1C))]-}${MouseButtons[$(( ${MouseBtn} & 0xe3))]}"
eval ${1}='"${MouseBtn}"'
}
mouse_on="$(vt100_DECSET ${mouse_type[1]})"
mouse_off="$(vt100_DECRST "${mouse_type[1]}" )"
function ReadKey {
unset UInput[@]
local escapeSequence
local REPLY
echo -n "${mouse_on}"
if IFS='' read -srN1 ${1:-} escapeSequence; then
case "${escapeSequence}" in
[^[:cntrl:]])
UInput[0]="${escapeSequence}"
;;
$'\e')
while IFS='' read -srN1 -t0.0001 ; do
escapeSequence+="${REPLY}"
done
case "${escapeSequence}" in
$'\e'[^[:cntrl:]]) echo -n "A-${escapeSequence:1}" ;;
${CSI}t)
UInput[0]="MouseTrack"
AdjustMousePos UInput[1] "${escapeSequence:3:1}"
AdjustMousePos UInput[2] "${escapeSequence:4:1}"
;;
${CSI}T)
UInput[0]="MouseTrack"
AdjustMousePos UInput[1] "${escapeSequence:3:1}"
AdjustMousePos UInput[2] "${escapeSequence:4:1}"
AdjustMousePos UInput[3] "${escapeSequence:5:1}"
AdjustMousePos UInput[4] "${escapeSequence:6:1}"
AdjustMousePos UInput[5] "${escapeSequence:7:1}"
AdjustMousePos UInput[6] "${escapeSequence:8:1}"
;;
${CSI}M*)
GetMouseButton UInput[0] "${escapeSequence:3:1}"
if [ -n "${UInput[0]}" ]; then
AdjustMousePos UInput[1] "${escapeSequence:4:1}"
AdjustMousePos UInput[2] "${escapeSequence:5:1}"
else
UInput[0]=$(printf 'Mouse-\\x%02x %q' "'${escapeSequence:3:1}" "${escapeSequence:4}")
fi
;;
${CSI}[0-9]*[ABCDEFHIOZPQRSz~])
local CSI_Params=( ${escapeSequence//[!0-9]/ } )
local CSI_Func="${escapeSequence:${#escapeSequence}-1}"
case "${CSI_Func}" in
z) # Sun Function Keys
UInput[0]="${SunKeybFntKeys[${CSI_Params[0]}]-}"
if [ -n "${UInput[0]}" ]; then
[ ${#CSI_Params[@]} -le 1 ] || UInput[0]="${KeyModifiers[${CSI_Params[1]}]}-${UInput[0]}"
else
UInput[0]="CSI ${CSI_Params[*]} ${CSI_Func}"
fi
;;
'~') # Function Keys
UInput[0]="${KeybFntKeys[${CSI_Params[0]}]-}"
if [ -n "${UInput[0]}" ]; then
[ ${#CSI_Params[@]} -le 1 ] || UInput[0]="${KeyModifiers[${CSI_Params[1]}]}-${UInput[0]}"
else
UInput[0]="CSI ${CSI_Params[*]} ${CSI_Func}"
fi
;;
A|B|C|D|E|F|H|I|O|Z|P|Q|R|S)
ord.eascii CSI_Func "${CSI_Func}"
UInput[0]="${KeybFntKeysAlt[${CSI_Func}]}"
if [ -n "${UInput[0]}" ]; then
[ ${#CSI_Params[@]} -le 1 ] || UInput[0]="${KeyModifiers[${CSI_Params[1]}]}-${UInput[0]}"
else
UInput[0]="CSI ${CSI_Params[*]} ${CSI_Func}"
fi
;;
*)
UInput[0]="CSI ${CSI_Params[*]} ${CSI_Func}"
;;
esac
;;
${SS3}*[ABCDEFHPQRSIO~])
local SS3_Params=( ${escapeSequence//[!0-9]/ } )
local SS3_Func="${escapeSequence:${#escapeSequence}-1}"
case "${SS3_Func}" in
A|B|C|D|E|F|H|P|Q|R|S|~)
ord.eascii SS3_Func "${SS3_Func}"
UInput[0]="${KeybFntKeysAlt[${SS3_Func}]-}"
if [ -n "${UInput[0]}" ]; then
[ ${#SS3_Params[@]} -lt 1 ] || UInput[0]="${KeyModifiers[${SS3_Params[0]}]}-${UInput[0]}"
else
UInput[0]="SS3 ${SS3_Params[*]-} ${SS3_Func}"
fi
;;
*)
UInput[0]="SS3 ${SS3_Params[*]-} ${SS3_Func}"
;;
esac
;;
$'\e'[[:cntrl:]])
ord.eascii UInput[0] "${escapeSequence:1:1}"
UInput[0]="${C1CtrlCharsAlt[${UInput[0]}]:-}"
[ -n "${UInput[0]:-}" ] || UInput[0]="$(printf "%q" "${escapeSequence}")"
;;
$'\e') UInput[0]="ESC" ;;
*)
UInput[0]="$(printf "%q" "${escapeSequence}")"
;;
esac
;;
*)
ord.eascii UInput[0] "${escapeSequence}"
UInput[0]="${C0CtrlChars[${UInput[0]}]:-}"
[ -n "${UInput[0]:-}" ] || UInput[0]="$(printf '%q' "'${escapeSequence}")"
;;
esac
fi
echo -n "${mouse_off}"
}
function HandleKey {
local -a UInput
while true; do
if ReadKey ; then
case "${UInput[0]:-}" in
CR|NULL|LF|q)
echo "\"${UInput[*]-}\""
break
;;
*)
echo "\"${UInput[*]-}\""
;;
esac
fi
done
}
HandleKey
Last edited by dethrophes; 01-07-2012 at 12:11 AM.
LinuxQuestions.org is looking for people interested in writing
Editorials, Articles, Reviews, and more. If you'd like to contribute
content, let us know.