keyboard buffer löschen in einem bashscript

Vom einfachen Programm zum fertigen Debian-Paket, Fragen rund um Programmiersprachen, Scripting und Lizenzierung.
Antworten
Benutzeravatar
puntarenas
Beiträge: 712
Registriert: 28.05.2005 15:07:05

keyboard buffer löschen in einem bashscript

Beitrag von puntarenas » 22.12.2005 14:05:30

Hallo,
zunächst ein simples Beispielscript:

Code: Alles auswählen

#!/bin/bash
while [[ ! $test = q ]]; do
read -s -n 1 test
sleep 5
done
Das Script funktioniert soweit, wie es soll. "read -s -n1" liest genau ein Zeichen von der Tastatur und speichert den Input in der Variable "test".
Danach legt sich das Script für 5 Sekunden schlafen und das Spiel beginnt von Neuem, bis irgendwann die Taste "q" gedrückt wird.

Leider hat die Sache einen Haken, wenn ich während der Wartezeit ungeduldig auf der Tastatur herumkloppe, füllt sich der Tastaturpuffer mit jeder Menge Input, der dann nach und nach im 5-Sekundenintervall von read eingelesen wird.

Daher wüsste ich gerne, wie ich den Tastaturpuffer vor einem erneuten "read" explizit löschen kann, also etwa so etwas:

Code: Alles auswählen

#!/bin/bash
while [[ ! $test = q ]]; do
DeleteKeyboardbufferAndSatisfyMySoul
read -s -n 1 test
sleep 5
done
Vielleicht kann man auch einfach anders an die Sache herangehen und somit das Problem vermeiden, wie auch immer, für eine Lösung wäre ich sehr dankbar.

Gruß
puntarenas

yeti

Beitrag von yeti » 23.12.2005 17:44:56

Lies doch vor dem eigentlichen Einlesen den Puffer leer mit

Code: Alles auswählen

read -s trash
also ohne die "-n 1"-Option.

Benutzeravatar
puntarenas
Beiträge: 712
Registriert: 28.05.2005 15:07:05

Beitrag von puntarenas » 23.12.2005 17:58:03

Funktioniert leider nicht, da dein "read -s thrash" ein <Enter> erwartet, bevor das Script fortgesetzt wird.
Im Moment behelfe ich mir bereits mit einem

Code: Alles auswählen

read -e -s -t 1 trash
Nach einer Sekunde wird automatisch der gesamte Puffer in $trash gespeichert, er ist also wieder leer.
Leider ist diese Lösung nicht ganz perfekt, da beispielsweise wildes Herumhacken auf <Enter> nicht abgefangen wird.
Außerdem erscheint es mir unwahrscheinlich, daß es keine Möglichkeit gibt den Puffer zu leeren.

Gruß
puntarenas

ToPeG
Beiträge: 437
Registriert: 14.04.2004 00:42:06

Beitrag von ToPeG » 24.12.2005 04:16:08

Versuch mal das. So würde ich es machen...

Code: Alles auswählen

#!/bin/bash
while [[ ! $test = q ]];
do
 trash=''
 while read -e -s -t 1 trash;
 do
  trash=''
 done
 read -p "INPUT:" -s -n 1 test
 sleep 5
done 

Benutzeravatar
puntarenas
Beiträge: 712
Registriert: 28.05.2005 15:07:05

Beitrag von puntarenas » 24.12.2005 13:11:21

Klasse, so funktioniert es. Auch bei einem Amoklauf auf <Enter> zeigt das Script jetzt das gewünschte Verhalten.

Kannst du mich mal schlau machen, wie der read Befehl genau arbeitet? Warum benötigt man die while-Schleife?

Beipspielsweise folgende Abwandlung:

Code: Alles auswählen

#!/bin/bash
i=0
while [[ ! $test = q ]]; do
while read -e -s -t 5 trash;
do
i=$[i+1]
echo $i
done
read -p "INPUT:" -s -n 1 test
echo "danke"
sleep 6
done

1) Das Script startet und wartet erst einmal 5 Sekunden, bis automatisch der Tastaturpuffer nach $trash geschrieben wird.
2) Input erscheint, das Script wartet auf eine einzige Taste, sagt "danke" und schläft dann 6 Sekunden.
3) Während dieser 6 Sekunden nutze ich die Zeit und kloppe wie ein Blöder 15x auf Enter.

Jetzt würde ich annehmen, daß read nach seinen 5 Sekunden entweder nur die letzte Eingabe ausliest und dann $i ausgibt, in dem Fall also 1, oder daß es nach jeweils 5 Sekunden eines der <Enter> ausliest, somit also erst nach 75 Sekunden fertig wäre.

Stattdessen erhalte ich aber sofort nachdem das Script aufwacht 15 Leerzeichen und dann die Ziffern 1 bis 15 ausgegeben. Dann wartet der erste read Befehl wieder 5 Sekunden, schreibt den Buffer nach $trash und weiter geht der Irrsinn.

Andererseits kann ich während das Script beim ersten read-Befehl die 5 Sekunden wartet mit <Enter> $i hochzählen und auf dem Bildschirm ausgeben.

Wenn du weißt warum, dann laß mich nicht dumm sterben. Wenn jemand noch eine direkte Lösung ohne Workaround weiß, ebenso.

Ansonsten ist deine Lösung durchaus befriedigend und für die Benutzerausgabe in einem Bashscript allemal ausreichend.

Tausend Dank,
puntarenas

ToPeG
Beiträge: 437
Registriert: 14.04.2004 00:42:06

Beitrag von ToPeG » 24.12.2005 14:21:52

"read" gibt im Erfolgsfall ein "true" zurück. Da <CR> die Zeile abschließt, ist auch die Eingabe beendet und "read" gibt true zurück. Wenn kein <CR> kommt wird alles bis dahin eingebene Eingelesen aber das Timeout tritt in Kraft und es wird ein "false" zurück gegeben. Damit beendet sich sie Schleife.
"read" liest nicht direckt den Tastaurpuffer sondern den Puffer der Shell, und deshalb sieht man auch die Eingabe, wenn kein "read" mit der option "-s" aktiv ist.

yeti

Beitrag von yeti » 24.12.2005 19:50:59

Alles, was ich tippe während "read -s" nicht aktiv ist, wird geechot und macht so die Benutzerführung kaputt.
Man muß wohl tatsächlich das ganze TTY auf NOECHO schalten, um liebevolle Bildschirmgestaltung nicht zu untergraben und den Benutzer nicht zu verwirren.

Das geht auch, "man stty", ... aber ist dann der Einsatz von "dialog" oder gar was mit Python und Ncurses selberschustern nicht einfacher?

Aber wenn schon TTY-Modi verdrehen, dann bitte am Programmende wieder zurücksetzen: trap "stty $ALTE_STTY_WERTE" EXIT
Die ursprünglichen Einstellungen kann man am Programmanfang mit "ALTE_STTY_WERTE=$(stty -g)" schnappschüsseln...

Auch noch keine befriedigende Antwort, ich weiß, aber vielleicht wieder ein oder zwei Puzzlesteine mehr...

Antworten