PHP CLI: как читать один символ ввода из TTY (не дожидаясь ввода ключа)?
Я хочу, чтобы каждый раз читал один символ из командной строки в PHP, однако кажется, что из-за чего-то там некоторая входная буферизация предотвращает это.
Рассмотрим этот код:
#!/usr/bin/php
<?php
echo "input# ";
while ($c = fread(STDIN, 1)) {
echo "Read from STDIN: " . $c . "\ninput# ";
}
?>
Вводя "foo" в качестве ввода (и нажав enter), вывод, который я получаю, следующий:
input# foo
Read from STDIN: f
input# Read from STDIN: o
input# Read from STDIN: o
input# Read from STDIN:
input#
Ожидаемый результат:
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#
(То есть, когда символы считываются и обрабатываются по мере их ввода).
Однако в настоящее время каждый символ считывается только после нажатия клавиши ввода. У меня есть подозрение, что TTY выполняет буферизацию ввода.
В конечном итоге я хочу читать клавиши, такие как стрелка ВВЕРХ, стрелка ВНИЗ и т.д.
Спасибо заранее!
Ответы
Ответ 1
Решением для меня было установить режим -icanon
на TTY (используя stty
). Например:.
stty -icanon
Итак, код, который теперь работает, это:
#!/usr/bin/php
<?php
system("stty -icanon");
echo "input# ";
while ($c = fread(STDIN, 1)) {
echo "Read from STDIN: " . $c . "\ninput# ";
}
?>
Вывод:
input# fRead from STDIN: f
input# oRead from STDIN: o
input# oRead from STDIN: o
input#
Read from STDIN:
input#
Подтверждает ответ, указанный здесь:
Есть ли способ подождать и получить нажатие клавиши с сеанса терминала (удаленного)?
Для получения дополнительной информации см.:
http://www.faqs.org/docs/Linux-HOWTO/Serial-Programming-HOWTO.html#AEN92
Не забудьте восстановить TTY, когда вы закончите с ним...
Восстановление конфигурации tty
Сброс терминала обратно до того, как это было сделано, можно сохранить, сохраняя состояние tty, прежде чем вносить в него изменения. После этого вы сможете восстановить это состояние.
Например:
<?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 . "'");
?>
Это единственный способ сохранить tty и вернуть его, как у него был пользователь до начала.
Обратите внимание, что если вы не беспокоитесь о сохранении исходного состояния, вы можете reset вернуться к стандартной "нормальной" конфигурации по умолчанию:
<?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");
?>
Ответ 2
Вот способ, который работает для меня с функциями readline и stream, без необходимости возиться с материалами 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;
}
}
Протестировано с помощью PHP 5.5.8 на OSX.
Ответ 3
Ниже приведена упрощенная версия ответа @seb, которая может использоваться для захвата одного символа. Он не требует stream_select
и использует readline_callback_handler_install
встроенную блокировку вместо создания цикла while. Он также удаляет обработчик для обеспечения дополнительного ввода как обычно (например, 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";
Ответ 4
<?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...";
}