Hi,
I'm sorry if this has already been posted somewhere but I can't seem to find it on the forums (or anywhere on google )
I am writing a script where a user must enter a single character to perform an action.
For example, Press Q to Quit or R to Refresh
Basically I am stuggling to come up with a way you can use the read function to read one character (without pressing enter or return) and then perform a command based on what character you entered.
The only solution I can find is read -n but this isn't available for me.
I've used the following code to perform a timed out read which will wait 1 second and then continue the code but it takes too long.
timedout_read() {
timeout=$1
varname=$2
old_tty_settings=`stty -g`
stty -icanon min 0 time ${timeout}0
eval read $varname
stty "$old_tty_settings"
}
timedout_read 1 TEST
Does anyone have any idea how to read single character without having to press enter?
I've found this piece of code in the past, it works with bash. Play around with it:
#!/bin/bash
_key()
{
local kp
ESC=$'\e'
_KEY=
read -d '' -sn1 _KEY
case $_KEY in
"$ESC")
while read -d '' -sn1 -t1 kp
do
_KEY=$_KEY$kp
case $kp in
[a-zA-NP-Z~]) break;;
esac
done
;;
esac
printf -v "${1:-_KEY}" "%s" "$_KEY"
}
_key x
case $x in
$'\e[11~' | $'\e[OP') key=F1 ;;
$'\e[12~' | $'\e[OQ') key=F2 ;;
$'\e[13~' | $'\e[OR') key=F3 ;;
$'\e[14~' | $'\e[OS') key=F4 ;;
$'\e[15~') key=F5 ;;
$'\e[16~') key=F6 ;;
$'\e[17~') key=F7 ;;
$'\e[18~') key=F8 ;;
$'\e[19~') key=F9 ;;
$'\e[20~') key=F10 ;;
$'\e[21~') key=F11 ;;
$'\e[22~') key=F12 ;;
$'\e[A' ) key=UP ;;
$'\e[B' ) key=DOWN ;;
$'\e[C' ) key=RIGHT ;;
$'\e[D' ) key=LEFT ;;
?) key=$x ;;
*) key=??? ;;
esac
echo "You have pressed $key"
try
echo -n "Enter character: "
stty cbreak
char=`dd if=/dev/tty bs=1 count=1 2>/dev/null`
stty -cbreak
echo " Character was: $char"
Thanks for the quick replies
Franklin52
When I try your method I get the following errors
./test.sh: read: illegal option: -d
read: usage: read [-r] [-p prompt] [-a array] [-e] [name ...]
./test.sh: printf: illegal option: -v
printf: usage: printf format [arguments]
You have pressed ???
funksen
When I try your method I get the following errors
unknown mode: cbreak
^[[B
unknown mode: -cbreak
It appears that I don't have the read -d, printf -v and cbreak functions.
Do you know of any other ways to do this?
try -icanon instead of -cbreak, if this doesn't work, use -raw
Edit: tried on AIX, raw worked
Thanks Funksen, raw works for me too!
---------- Post updated at 01:31 PM ---------- Previous update was at 12:54 PM ----------
Sorry to bother you again.
When I tried this previously, I just had a standard read command.
So I would press the key and then press return and the command would be fired.
Now that have the below code instead, I can't seem to capture the keypresses correctly
stty raw
sel=`dd if=/dev/tty bs=1 count=1 2>/dev/null`
stty -raw
I used to have
read sel
case $sel in
1) KEY=1 ; NUM=$sel
;;
2) KEY=2 ; NUM=$sel
;;
3) KEY=3 ; NUM=$sel
;;
4) KEY=4 ; NUM=$sel
;;
5) KEY=5 ; NUM=$sel
;;
6) KEY="" ; QUIT="Y"
;;
["Q","q"]) KEY="" ; QUIT="Y"
;;
$'\e[A' )
if [ $NUM -eq 1 ]
then
NUM=6
else
NUM=$(($NUM - 1))
fi
;;
$'\e[B' )
if [ $NUM -eq 6 ]
then
NUM=1
else
NUM=$(($NUM + 1))
fi
;;
"") KEY=$NUM
;;
*) echo "ERROR"
;;
esac
This would determine whether the user was pressing up and down, q, a number or just pressing enter.
Now when I use the same code it isn't recognising the up, down or enter keypresses.
Have you any idea what I should change to get it to work.
Just to clarify, my code looks like the following.
echo -n "Choose one: "
stty raw
sel=`dd if=/dev/tty bs=1 count=1 2>/dev/null`
stty -raw
case $sel in
1) KEY=1 ; NUM=$sel
;;
2) KEY=2 ; NUM=$sel
;;
3) KEY=3 ; NUM=$sel
;;
4) KEY=4 ; NUM=$sel
;;
5) KEY=5 ; NUM=$sel
;;
6) KEY="" ; QUIT="Y"
;;
["Q","q"]) KEY="" ; QUIT="Y"
;;
$'\e[A' )
if [ $NUM -eq 1 ]
then
NUM=6
else
NUM=$(($NUM - 1))
fi
;;
$'\e[B' )
if [ $NUM -eq 6 ]
then
NUM=1
else
NUM=$(($NUM + 1))
fi
;;
"") KEY=$NUM
;;
*) echo "ERROR"
;;
esac
Any suggestions would be much appreciated.