getch unter Linux
-
Ziel war es, ein Ruby-Skript um eine interaktive Komponente zu erweitern: beim Drücken von 's' sollte die aktuelle Aufgabe übersprungen und zur nächsten weiter gegangen werden. Um nicht "s<Enter>" tippen zu müssen, brauchte man sowas wie
getch
. Ein bisschen Recherche zeigte: Das gab es auch.require 'io/console' c = STDIN.getch if c == "s" … end
Ungünstig jedoch: das
getch
bringt das Terminal in den Raw-Modus, womit auch z.B. Ctrl+C das Skript nicht mehr wie gewünscht beendet, sondern als Zeichen weitergegeben wird. Offenbar gibt es auch nur Raw oder Nicht-Raw, kein Mittelding mit ungepufferter Eingabe aber nach wie vor intaktem Kontrollfluss.Also simulieren.
exit if c.getbyte(0) == 3
Nur blöd, wenn das Skript z.B. in einem anderen Shellskript aufgerufen wird. Dann wird das äußere Skript nicht beendet.
Man fragt sich, wie das normalerweise geht. Vielleicht fragt die Shell ja ab, ob der Befehl durch ein Signal beendet wurde.
Process.kill "SIGINT", Process.pid
Nun, tut sie nicht.
Da das SIGINT aber von der Shell auch nicht andersrum an das Skript weitergeleitet wird (wie experimentell bestätigt), muss das Ctrl+C als SIGINT irgendwie an Shell und Kinder verschickt werden, offenbar vom Terminal. Also muss man dieses vielleicht einfach nur von dem Ctrl+C in Kenntnis setzen, nachdem man den Raw-Modus wieder beendet hat.
STDOUT.putc c
Nö.
File.open "/dev/tty", "w" { |f| f.write c }
Auch nicht.
Die Lösung war schließlich der Aufruf eines kleinen Hilfsprogramms folgenden Inhalts:
import System.IO main = hSetBuffering stdin NoBuffering >> getChar >>= putChar
Nur: wie macht das Haskell? Sämtliche Versuche mit dem Haskell-Programm zeigen ein identisches Verhalten, wie wenn man Ctrl+C im Nicht-Raw-Modus gedrückt hätte (inklusive Beenden des aufrufenden Skriptes und dessen aufrufenden Skriptes usw.). Und kann es so schwer sein, ein Zeichen ungepuffert einzulesen, ohne gleich den Kontroll-Fluss auszuschalten? Wie sieht generell die schöne Lösung aus und kann man diese in Ruby umsetzen?