BASH: Tastatur abfragen

Markus Everson

Grand Admiral Special
Mitglied seit
22.10.2004
Beiträge
6.787
Renomée
270
Standort
Deutschland
Hallo Welt,

was sich wie eine der einfachsten Übungen anhört ist beim genauen hinsehen scheinbar doch wieder unendlich kompliziert.
Für mein Programm wollte ich ursprünglich nur den Cursorblock nutzen. Soweit, so gut. Bin dann aber darüber gestolpert das die gleiche Taste je nach Terminal und OS-derivat unterschiedliches Verhalten zeigt.

längere Sessions mit Tante google führen schließlich zu einem Artikel in Dr. Dobbs und folgendem Testscript:

Code:
#!/bin/bash
# keytest.sh - test keyscan and cursor keys
#
trap 'stty sane; printf "${CSI}?12l${CSI}?25h\e[0m\n\n"' EXIT

stty -echo   ## Turn off echoing of user keystrokes
. gk-funcs   ## Source key functions
clear        ## Clear the screen

do_loop()
{
	printf "\e[%d;%dH" "$row" "$col"
        printf "  Press 'q' to quit\n" 
}

## Initial position for text block
row=$(( (${LINES:-24} - 10) / 2 ))
col=$(( (${COLUMNS:-80} - ${#bar}) / 2 ))

## Initial colours
fg="${CSI}33m"
bg="${CSI}44m"

## Turn off cursor
printf "%s" "${CSI}?25l"

## Loop until user presses "q"
while :
do
  do_loop
  _get_key
  # only for Test 
  printf "$swiper\r_KEY= $_KEY\n"
  echo "2: ` echo -n $_KEY | od -x | head -1 `                      "

  #
  case $_KEY in
      RET)   printf "$swiper\rRETURN Key has been found" ;;
      UP)    printf "$swiper\rdrop some code for cursor up" ;;
      q|Q) break ;;
      *)     echo "                                      " ;;
  esac
done

Code:
## this is based on gk_funcs found in http://www.drdobbs.com/shell-corner-reading-function-and-cursor/199102881
## Define useful variables

  ## To help emacs with syntax highlighting
  bra='['
  ket=']'

  ESC=$'\e'   ## ESC=$( printf "\033" )
  CSI=${ESC}$bra
  NA=${CSI}0m

  Ca=$'\001' Cb=$'\002' Cc=$'\003' Cd=$'\004' Ce=$'\005' Cf=$'\006'
  Cg=$'\a'   Ch=$'\010' Ci=$'\012' Cj=$'\n'   Ck=$'\014' Cl=$'\f'
  Cm=$'\r'   Cn=$'\016' Co=$'\017' Cp=$'\020' Cq=$'\021' Cr=$'\022'
  Cs=$'\023' Ct=$'\024' Cu=$'\025' Cv=$'\026' Cw=$'\027' Cx=$'\030'
  Cy=$'\031' Cz=$'\032'

# for screenplay
  swiper="                            "
  swiper=$swiper$swiper$swiper
#

_key() ## Get one character from stdin
{
    IFS= read -r -s -n1 -d '' "${@:-_KEY}"
}

_keys() ## Store all waiting keypresses in $_KEYS
{
    _KEYS=
    __KX=

    ## ESC_END is a list of characters that can end a key sequence
    ## Some terminal emulations may have others; adjust to taste
    ESC_END=[a-zA-NP-Z~^\$@$ESC]     # das original

    while :
    do
      IFS= read -r -s -n1 -d '' -t1 __KX
      _KEYS=$_KEYS$__KX
      case $__KX in
          "" | $ESC_END ) break ;;
      esac
    done
}

_esc2key() ## Convert escape sequences to key names
{
  case $1 in
    ## Cursor keys
    "$CSI"A | ${ESC}OA ) _ESC2KEY=UP ;;
    "$CSI"B | ${ESC}OB ) _ESC2KEY=DOWN ;;
    "$CSI"C | ${ESC}OC ) _ESC2KEY=RIGHT ;;
    "$CSI"D | ${ESC}OD ) _ESC2KEY=LEFT ;;

    ## Function keys (unshifted)
    "$CSI"11~ | "$CSI["A | ${ESC}OP ) _ESC2KEY=F1 ;;
    "$CSI"12~ | "$CSI["B | ${ESC}OQ ) _ESC2KEY=F2 ;;
    "$CSI"13~ | "$CSI["C | ${ESC}OR ) _ESC2KEY=F3 ;;
    "$CSI"14~ | "$CSI["D | ${ESC}OS ) _ESC2KEY=F4 ;;
    "$CSI"15~ | "$CSI["E ) _ESC2KEY=F5 ;;
    "$CSI"17~ | "$CSI["F ) _ESC2KEY=F6 ;;
    "$CSI"18~ ) _ESC2KEY=F7 ;;
    "$CSI"19~ ) _ESC2KEY=F8 ;;
    "$CSI"20~ ) _ESC2KEY=F9 ;;
    "$CSI"21~ ) _ESC2KEY=F10 ;;
    "$CSI"23~ ) _ESC2KEY=F11 ;;
    "$CSI"24~ ) _ESC2KEY=F12 ;;

    ## Insert, Delete, Home, End, Page Up, Page Down
    "$CSI"2~ ) _ESC2KEY=INS ;;
    "$CSI"3~ ) _ESC2KEY=DEL ;;
    "$CSI"[17]~ | "$CSI"H ) _ESC2KEY=HOME ;;
    "$CSI"[28]~ | "$CSI"F ) _ESC2KEY=END ;;
    "$CSI"5~ ) _ESC2KEY=PGUP ;;
    "$CSI"6~ ) _ESC2KEY=PGDN ;;

    ## Everything else; add other keys before this line
    *) _ESC2KEY=UNKNOWN ;;
  esac
  [ -n "$2" ] && eval "$2=\$_ESC2KEY"

}

_get_key()
{
    _key
printf "$swiper\r1: "; echo -n $_KEY | od -x | head -1 
    case $_KEY in
        "$ESC") _keys
                _esc2key "$ESC$_KEYS" _KEY
                ;;
    esac
}

Ich hab in ewig langer Bastelei letztlich nur extrem wenig geändert. Die beiden Scripte aus dem Original nur soweit reduziert bis es lediglich die Keyscans ausgibt.

Mein Problem: nachaffen und verstehen sind zwei paar Schuhe. Ich komme einfach nicht dahinter wie ich Return und Space abfragen kann. Jemand eine Idee?
 
Ich hatte ursprünglich nur den Cursorblock im Sinn, habe aber während der Entwicklung meines Programmes weitere Tasten zufügen wollen. Also suchte ich eine Methode zum Lesen aller möglichen Keys. Warum nicht "einfach" read ist im Artikel recht überzeugend erklärt:

http://www.drdobbs.com/shell-corner-reading-function-and-cursor/199102881 schrieb:
It's a useful function by itself; it can store a space, a newline, a control code, or any other single character. What it doesn't do is handle keys that return more than one character: function keys, cursor keys, and a few others. These special keys return ESC (0x1B, which I keep in a variable $ESC) followed by one or more characters. The number of characters varies according to the key (and the terminal emulation), so we cannot ask for a specific number of keys.
 
Ich hab auch noch nicht so richtig verstanden, was du eigentlich erreichen willst. Willst du ein (Auswahl)Menü (nach)bauen? Dann ist vielleicht dialog das Richtige für dich. Oder welchen anderen Grund hat es, die Cursortasten und/oder F-Tasten benutzen zu wollen?

MfG Dalai
 
Falls du so spezielle Sachen machen willst, wirst du - wie in dem Quote angedeutet - auch Binärwerte der Eingaben auswerten müssen. Da wirst du mit Bash oder generell auf Shell-Ebene keinen Spaß haben. Dort ist alles auf die Verarbeitung von Text getrimmt.

Ich vermute, du machst dir da das Leben unnötig schwer, aber falls du wirklich sowas bauen musst, dann solltest du eine passende Sprache wählen.

Nach deinem ersten Post soll es portabel sein, da würde ich spontan "Perl" sagen. Vorteil ist, es ist immer mitinstalliert - nur bei den Modulen könnte es dann haarig werden.
Oder eben eine andere Skriptsprache.

Falls Dalai mit der Menü-Vermutung richtig liegt: Warum nicht gleich eine GUI? Da wäre dann Java (wegen Portabilität) das Mittel der Wahl.


Oder du beschreibst doch mal etwas ausführlicher, was das werden soll. So stochert man nur im Nebel.
 
Ich danke euch allen dafür das ihr euch soviel Mühe gebt.

Letztlich ist mein Ziel aber bereits klar umrissen. Ich will mir die Möglichkeit schaffen in einem Bash-Script alle möglichen Tasten inklusive Return zu nutzen. Ich bin in Bash nun wirklich nicht besonders gut, aber allemal besser als in Java. (Perl ebenfalls bereits anderweitig versucht und verzweifelt, geht einfach nicht an mich ran)
 
Eventuell musst du direkt /dev/stdin ansprechen? (Linux only erstmal)
Hab ich aber noch nie gemacht, vielleicht bringt dich das aber in die richtige Richtung.
 
Zurück
Oben Unten