modo linea interactivo ingresar how ejecutar desde consola como comandos comando cli php stdin command-line-interface buffering tty

linea - CLI de PHP: ¿Cómo leer un solo carácter de entrada desde el TTY(sin esperar la tecla de entrada)?



php interactive cli (4)

Quiero leer un solo carácter a la vez desde la línea de comandos en PHP, sin embargo, parece que hay algún tipo de búfer de entrada que impide que esto ocurra.

Considere este código:

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

Al escribir "foo" como entrada (y presionar intro), la salida que obtengo es:

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

La salida que estoy esperando es:

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#

(Es decir, con los caracteres que se leen y procesan a medida que se escriben).

Sin embargo, actualmente, cada carácter se lee solo después de presionar Intro. Tengo la sospecha de que el TTY está almacenando en búfer la entrada.

En última instancia, quiero poder leer las pulsaciones de teclas, como la flecha ARRIBA, la flecha ABAJO, etc.


Esta es una manera que funciona para mí con las funciones readline y stream, sin necesidad de meterme con cosas tty.

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; } }

Probado con PHP 5.5.8 en OSX.


La siguiente función es una versión simplificada de la respuesta de @ seb que se puede usar para capturar un solo carácter. No requiere stream_select , y usa el bloqueo inherente de readline_callback_handler_install lugar de crear un bucle while. También elimina el controlador para permitir una entrada adicional de forma normal (como readline).

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";


La solución para mí fue establecer el modo -icanon en el TTY (usando stty ). P.ej.:

stty -icanon

Entonces, el código que ahora funciona es:

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

Salida:

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

Apoyos a la respuesta dada aquí:
¿Hay alguna forma de esperar y presionar una tecla desde una sesión de terminal (remota)?

Para más información, ver:
http://www.faqs.org/docs/Linux-HOWTO/Serial-Programming-HOWTO.html#AEN92

No olvides restaurar el TTY cuando hayas terminado con él ...

Restaurando la configuración tty

Restablecer el terminal de nuevo a la forma en que estaba se puede hacer guardando el estado tty antes de realizar cambios en él. A continuación, puede restaurar a ese estado cuando haya terminado.

Por ejemplo:

<?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 . "''"); ?>

Esta es la única forma de preservar el tty y volver a ponerlo como el usuario lo tenía antes de comenzar.

Tenga en cuenta que si no está preocupado por preservar el estado original, puede restablecerlo de nuevo a una configuración "sana" predeterminada simplemente haciendo lo siguiente:

<?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"); ?>


<?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..."; }