[comp.unix.questions] How do I detect a keyboard press?

cadp35@vaxa.strath.ac.uk (05/02/91)

any of you wiz's know how I can detect a keyboard press in shell script??

I want it for a script which will execute a certain option if I press the
correct key.

toma@swsrv1.cirr.com (Tom Armistead) (05/03/91)

In article <1991May2.125554.11557@vaxa.strath.ac.uk> cadp35@vaxa.strath.ac.uk writes:
>any of you wiz's know how I can detect a keyboard press in shell script??
>
>I want it for a script which will execute a certain option if I press the
>correct key.


Here is my shot at reading individual characters from the terminal from within
a shell script.  There are definately more 'elegant' solutions, but this was
done using ONLY sh (and it was actually fun!).

This works on System Vr3.2.  It most probally won't work on other Unix's
(System V is all I know...)? If you're system doesn't have the 'hd' command,
replace the line:

    key=`hd -n 1 -t|awk '{ printf "%s", $2; }'`

With:

    key=`rd`

And here is the C source for the rd command (rd.c). To compile it:
    $ cc -o rd rd.c
    
    main() { char ch; read(0,&ch,1); write(1,&ch,1); }

In summary, this is what I did.

I used the stty command to put the terminal in non-cononical mode (-icanon)
and then used the stty command to set the VTIME (time to wait for a character)
and VMIN (# of characters to read before returning to caller) so that a read
would wait forever for a single character (See the termio(7) man page).  Then
I used the hd (hex dump) command (or rd if you don't have hd) to read a
single character from the keyboard.  The shell read command wouldn't work,
because it reads until it see's a \n character.

I used stty -g, which prints out values from the 'termio' structure (in the
order that they appear in that structure).  I then used awk to modify the
needed elements in that structure to get a VMIN of 1 and a VTIME of 0.
I would of been able to use "stty eof '^a' eol '^@'" if ^@ evaluated to a hex
0 (eof and eol correspond to the VMIN and VTIME termio elements when in
-icanon mode).


Any questions?
Tom
-- 
Tom Armistead - Software Services - 2918 Dukeswood Dr. - Garland, Tx  75040
===========================================================================
toma@swsrv1.cirr.com                {egsner,letni,ozdaltx,void}!swsrv1!toma


############ Cut all above this line #########################################
#
# This source code is public domain.  Use it as YOU see fit.  If you want to
# send me money, the fridge is a little low on beer.  Or you could just send
# me beer (I drink Stroh's Lite)...
#
##############################################################################
#
# Save the current stty settings.
#
Orig_tty=`stty -g`

# Turn of Erase/Kill/INTR processing (enable single character read)
# This is the stty mode that will be used to read the character from the
# terminal.  If you don't want the pressed keys to be echo'd to the terminal,
# add '-echo' to this stty command.
#
stty -icanon -isig -brkint	# Tty mode to read character in.
Raw_tty=`stty -g`		# Save stty settings for the new mode.
stty $Orig_tty			# Restore original stty settings.

# Set single character read (the "1") and wait forever for the character (the
# "0")  These positions correspond to VMIN and VTIME in the termio structure
# (positions 4 and 5 in termio.c_cc[]).
#
Raw_tty=`echo $Raw_tty|awk -F: '{
	               printf "%s:%s:%s:%s:%s:%s:%s:%s:%s:%s:%s:%s", \
                       $1,$2,$3,$4,$5,$6,$7,$8,"1","0",$11,$12 \
	               }'`



while [ "$leave" != yes ]
do
    echo
    echo "1   - Do the 1 thing."
    echo "2   - Do the 2 thing."
    echo "3   - Do the 3 thing."
    echo "4   - Do the 4 thing."
    echo "Q   - Quit"

    echo "\nSelect: \c"

    # Set up terminal to read read a single character
    #
    stty $Raw_tty

    # Read a single key from the terminal
    #
    key=`hd -n 1 -t|awk '{ printf "%s", $2; }'`

    # Restore original terminal settings
    #
    stty $Orig_tty

    # Figure out what to do with the pressed key?
    #
    case $key in
        1   )   echo "\n\nFirst thing."
                ;;
	2   )   echo "\n\nSecond thing."
                ;;
	3|4 )   echo "\n\nThree or Four."
                ;;
	q|Q )   echo "\n\nSo long..."
		leave=yes
                ;;
	*   )   echo "\n\n\007INVALID OPTION.\n"
                ;;
    esac
done #while

# The END.
-- 
Tom Armistead - Software Services - 2918 Dukeswood Dr. - Garland, Tx  75040
===========================================================================
toma@swsrv1.cirr.com                {egsner,letni,ozdaltx,void}!swsrv1!toma

daniel@island.COM (Daniel Smith "innovation, not litigation...") (05/07/91)

In <1991May3.070038.3659@swsrv1.cirr.com> toma@swsrv1.cirr.com (Tom Armistead) writes:

> In article <1991May2.125554.11557@vaxa.strath.ac.uk> cadp35@vaxa.strath.ac.uk writes:
> >any of you wiz's know how I can detect a keyboard press in shell script??
> >
> >I want it for a script which will execute a certain option if I press the
> >correct key.


	One way is to get my grabchars package from comp.sources.misc archives,
which was written to do just this.  You can also get it directly from
me.  I also include scripts which can generate skeletons of menuing
scripts.

	Another thing you can do is this:

	(reassemble this as one line...)

        alias   get_choice 'set choice_prompt=(\!*); echo -n "$choice_prompt"; s
tty cbreak ; set choice=`echo "dd if=/dev/tty bs=1 count=1 2>/dev/null"|sh|cat -
v` ; stty -cbreak'

	as in:

	get_choice '        your choice >> '

        if ("$choice" =~ '?') set choice=help
        if ("$choice" =~ '^[') set choice=escape
        switch ($choice)
		bla bla bla...
	endsw


				Daniel

-- 
daniel@island.com       Daniel Smith, Island Graphics, (415) 491 0765 x 250(w)
daniel@world.std.com      4000 CivicCenterDrive SanRafael MarinCounty CA 94903
dansmith@well.sf.ca.us      Fax: 491 0402 Disclaimer: Hey, I wrote it, not IG!
falling/yes I'm falling/and she keeps calling/me back again - IJSaF, Beatles