2010-09-10 7 views
22

Ich möchte ein einzelnes Zeichen von der Kommandozeile in PHP lesen, aber es scheint, als ob irgendwo eine Art von Pufferung stattfindet, um dies zu verhindern.PHP CLI: Wie liest man ein einzelnes Zeichen der Eingabe vom TTY (ohne auf die Eingabetaste zu warten)?

Betrachten Sie diesen Code:

#!/usr/bin/php 
<?php 
echo "input# "; 
while ($c = fread(STDIN, 1)) { 
    echo "Read from STDIN: " . $c . "\ninput# "; 
} 
?> 

Typing in "foo" als Eingangs (und die Eingabetaste drücken), die Ausgabe erhalte ich ist:

input# foo 
Read from STDIN: f 
input# Read from STDIN: o 
input# Read from STDIN: o 
input# Read from STDIN: 

input# 

Der Ausgang I erwarte ich ist:

input# f 
input# Read from STDIN: f 

input# o 
input# Read from STDIN: o 

input# o 
input# Read from STDIN: o 

input# 
input# Read from STDIN: 

input# 

(Das heißt, mit Zeichen wird rea d und verarbeitet, wie sie eingegeben werden).

Derzeit wird jedoch jedes Zeichen nur gelesen, nachdem Enter gedrückt wurde. Ich habe den Verdacht, dass das TTY die Eingabe puffert.

Letztlich mag ich in der Lage sein, Drücken von Tasten wie Pfeil nach oben zu lesen, Pfeil nach unten usw.

Vielen Dank im Voraus!

Antwort

30

Die Lösung für mich war -icanon Modus auf dem TTY (mit stty). ZB .:

stty -icanon 

Also, die der Code, jetzt funktioniert, ist:

#!/usr/bin/php 
<?php 
system("stty -icanon"); 
echo "input# "; 
while ($c = fread(STDIN, 1)) { 
    echo "Read from STDIN: " . $c . "\ninput# "; 
} 
?> 

Ausgang:

input# fRead from STDIN: f 
input# oRead from STDIN: o 
input# oRead from STDIN: o 
input# 
Read from STDIN: 

input# 

Requisiten auf die Antwort hier gegeben:
Is there a way to wait for and get a key press from a (remote) terminal session?

Weitere Informationen finden Sie unter:
http://www.faqs.org/docs/Linux-HOWTO/Serial-Programming-HOWTO.html#AEN92

Vergessen Sie nicht, die TTY wiederherzustellen, wenn Sie mit ihm fertig sind ...

die tty Konfiguration wiederherstellen

Zurücksetzen des Terminal zurück auf die Art und Weise war es kann sein, Fertig, indem Sie den tty-Status speichern, bevor Sie Änderungen daran vornehmen. Sie können dann in diesem Zustand wiederherstellen, wenn Sie fertig sind.

Zum Beispiel:

<?php 

// Save existing tty configuration 
$term = `stty -g`; 

// Make lots of drastic changes to the tty 
system("stty raw opost -ocrnl onlcr -onocr -onlret icrnl -inlcr -echo isig intr undef"); 

// Reset the tty back to the original configuration 
system("stty '" . $term . "'"); 

?> 

Dies ist der einzige Weg, um die tty zu bewahren und legt sie zurück, wie der Benutzer es hatte, bevor Sie begann.

Beachten Sie, dass, wenn Sie nicht besorgt sind über den ursprünglichen Zustand zu bewahren, können Sie es zurück zu einem Standard zurücksetzen „sane“ Konfiguration einfach, indem Sie:

<?php 

// Make lots of drastic changes to the tty 
system("stty raw opost -ocrnl onlcr -onocr -onlret icrnl -inlcr -echo isig intr undef"); 

// Reset the tty back to sane defaults 
system("stty sane"); 

?> 
+0

„Vergessen Sie nicht die TTY zurückzusetzen, wenn Sie mit ihm fertig sind!“ - Wie setzen wir es zurück? – mpen

+3

Mark: Am einfachsten können Sie 'stty vernünftig' machen. Um jedoch zu garantieren, dass Sie das tty auf den exakten Zustand zurücksetzen, sollten Sie vorher den Zustand speichern. Ich habe die Antwort um Beispiele erweitert, wie man das macht. – dansimau

+0

schön! Danke dafür :) – mpen

20

Hier ist ein Weg, der für mich arbeitet mit readline und stream Funktionen, ohne sich mit tty stuff herumschlagen zu müssen.

readline_callback_handler_install('', function() { }); 
while (true) { 
    $r = array(STDIN); 
    $w = NULL; 
    $e = NULL; 
    $n = stream_select($r, $w, $e, null); 
    if ($n && in_array(STDIN, $r)) { 
    $c = stream_get_contents(STDIN, 1); 
    echo "Char read: $c\n"; 
    break; 
    } 
} 

Getestet mit PHP 5.5.8 auf OSX.

+4

Dies sollte die akzeptierte Antwort sein. – h0tw1r3

+1

readline muss in PHP kompiliert werden. Überprüfen Sie Ihre Konfiguration für die '' '--with-readline =/opt/local''' mit diesem Befehl:' '' php -i | grep readline''' – arikin

+1

Verwendung (sehr kürzlich aktualisiert) Arch Linux und PHP 7.0.5. Der "Bruch" verursachte das Lesen eines Chars und dann Stall; das Entfernen hat alles großartig gemacht. Ich habe auch die '0' am Ende des' select' in ein 'NULL' geändert, und jetzt verwendet PHP nicht 100% CPU (!). * Diese Funktion scheint perfekt auf meinem Rechner zu funktionieren (Erfassung von '\ 033's und allem) basierend auf ersten Tests. * –

2

Die folgende Funktion ist eine vereinfachte Version der @ seb-Antwort, die zum Erfassen eines einzelnen Zeichens verwendet werden kann. Es erfordert nicht stream_select und verwendet readline_callback_handler_install 's inhärente Blockierung, anstatt eine while-Schleife zu erstellen. Außerdem wird der Handler entfernt, um weitere Eingaben wie normal (z. B. readline) zu ermöglichen.

function readchar($prompt) 
{ 
    readline_callback_handler_install($prompt, function() {}); 
    $char = stream_get_contents(STDIN, 1); 
    readline_callback_handler_remove(); 
    return $char; 
} 

// example: 
if (!in_array(
    readchar('Continue? [Y/n] '), ["\n", 'y', 'Y'] 
    // enter/return key ("\n") for default 'Y' 
)) die("Good Bye\n"); 
$name = readline("Name: "); 
echo "Hello {$name}.\n"; 
-1
<?php 
`stty -icanon`; 
// this will do it 
stream_set_blocking(STDIN, 0); 
echo "Press 'Q' to quit\n"; 
while(1){ 
    if (ord(fgetc(STDIN)) == 113) { 
     echo "QUIT detected..."; 
     break; 
    } 
    echo "we are waiting for something..."; 
} 
+0

Es scheint nicht zu funktionieren. Im 'stream_set_blocking'-Handbuch heißt es:' Diese Funktion funktioniert für jeden Stream, der den nicht-blockierenden Modus unterstützt (derzeit normale Dateien und Socket-Streams). "Ich fürchte, dass STDIN dort nicht hingehört. – DrLightman

+0

müssen Sie stty -icanon im Terminal einstellen und es funktioniert gut .. – joeldg

+1

Vielen Dank für dieses Code-Snippet, das einige begrenzte, sofortige Hilfe bieten kann. Eine [richtige Erklärung] (https://meta.stackexchange.com/q/114762) würde ihren langfristigen Wert erheblich verbessern, indem sie zeigt, warum dies eine gute Lösung für das Problem ist und es für zukünftige Leser mit anderen nützlicher machen würde , ähnliche Fragen. Bitte [bearbeiten] (https://meta.stackoverflow.com/posts/360251/edit) Ihre Antwort, um einige Erklärungen hinzuzufügen, einschließlich der Annahmen, die Sie getroffen haben. [ref] (https://meta.stackoverflow.com/a/360251/8371915) – user8371915

Verwandte Themen