PHP CLI:如何从TTY中读取输入的单个字符(无需等待回车键)?

dgiusagp  于 2023-06-04  发布在  PHP
关注(0)|答案(5)|浏览(226)

我想从PHP的命令行中一次读取一个字符,但是似乎有某种输入缓冲阻止了这一点。
考虑以下代码:

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

输入“foo”作为输入(并按回车键),我得到的输出是:

  1. input# foo
  2. Read from STDIN: f
  3. input# Read from STDIN: o
  4. input# Read from STDIN: o
  5. input# Read from STDIN:
  6. input#

expecting 的输出是:

  1. input# f
  2. input# Read from STDIN: f
  3. input# o
  4. input# Read from STDIN: o
  5. input# o
  6. input# Read from STDIN: o
  7. input#
  8. input# Read from STDIN:
  9. input#

(That在键入字符时读取和处理字符)。
但是,当前,每个字符仅在按下回车键后才被读取。我怀疑TTY正在缓冲输入。
最终,我希望能够阅读按键,如向上箭头,向下箭头等。

wsxa1bj1

wsxa1bj11#

对我来说,解决方案是在TTY上设置-icanon模式(使用stty)。例如:

  1. stty -icanon

所以,现在起作用的代码是:

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

输出:

  1. input# fRead from STDIN: f
  2. input# oRead from STDIN: o
  3. input# oRead from STDIN: o
  4. input#
  5. Read from STDIN:
  6. input#

支持这里给出的答案:
是否有一种方法可以等待并从(远程)终端会话获得按键?
有关详细信息,请参阅:
http://www.faqs.org/docs/Linux-HOWTO/Serial-Programming-HOWTO.html#AEN92
不要忘记恢复TTY当你完成它...

恢复tty配置

将终端重置回原来的状态可以通过在更改tty状态之前保存它来完成。然后,您可以在完成后恢复到该状态。
例如:

  1. <?php
  2. // Save existing tty configuration
  3. $term = `stty -g`;
  4. // Make lots of drastic changes to the tty
  5. system("stty raw opost -ocrnl onlcr -onocr -onlret icrnl -inlcr -echo isig intr undef");
  6. // Reset the tty back to the original configuration
  7. system("stty '" . $term . "'");
  8. ?>

这是保存tty并将其放回用户在开始之前拥有的状态的唯一方法。
请注意,如果您不担心保留原始状态,您可以通过执行以下操作将其重置回默认的“正常”配置:

  1. <?php
  2. // Make lots of drastic changes to the tty
  3. system("stty raw opost -ocrnl onlcr -onocr -onlret icrnl -inlcr -echo isig intr undef");
  4. // Reset the tty back to sane defaults
  5. system("stty sane");
  6. ?>
展开查看全部
ou6hu8tu

ou6hu8tu2#

这里有一种方法,它适用于readline和stream函数,而不需要弄乱tty的东西。

  1. readline_callback_handler_install('', function() { });
  2. while (true) {
  3. $r = array(STDIN);
  4. $w = NULL;
  5. $e = NULL;
  6. $n = stream_select($r, $w, $e, null);
  7. if ($n && in_array(STDIN, $r)) {
  8. $c = stream_get_contents(STDIN, 1);
  9. echo "Char read: $c\n";
  10. break;
  11. }
  12. }

在OSX上用PHP 5.5.8测试。

tvokkenx

tvokkenx3#

下面的函数是@seb的答案的简化版本,可用于捕获单个字符。它不需要stream_select,并使用readline_callback_handler_install固有的阻塞而不是创建while循环。它还删除了处理程序,以允许进一步的正常输入(如readline)。

  1. function readchar($prompt)
  2. {
  3. readline_callback_handler_install($prompt, function() {});
  4. $char = stream_get_contents(STDIN, 1);
  5. readline_callback_handler_remove();
  6. return $char;
  7. }
  8. // example:
  9. if (!in_array(
  10. readchar('Continue? [Y/n] '), ["\n", 'y', 'Y']
  11. // enter/return key ("\n") for default 'Y'
  12. )) die("Good Bye\n");
  13. $name = readline("Name: ");
  14. echo "Hello {$name}.\n";
lndjwyie

lndjwyie4#

  1. <?php
  2. `stty -icanon`;
  3. // this will do it
  4. stream_set_blocking(STDIN, 0);
  5. echo "Press 'Q' to quit\n";
  6. while(1){
  7. if (ord(fgetc(STDIN)) == 113) {
  8. echo "QUIT detected...";
  9. break;
  10. }
  11. echo "we are waiting for something...";
  12. }
wj8zmpe1

wj8zmpe15#

下面的函数将等待直到用户输入一个字符,然后立即返回。此方法支持多字节字符,因此也适用于检测箭头键按下。

  1. function waitForInput(){
  2. $input = '';
  3. $read = [STDIN];
  4. $write = null;
  5. $except = null;
  6. readline_callback_handler_install('', function() {});
  7. // Read characters from the command line one at a time until there aren't any more to read
  8. do{
  9. $input .= fgetc(STDIN);
  10. } while(stream_select($read, $write, $except, 0, 1));
  11. readline_callback_handler_remove();
  12. return $input;
  13. }

下面是使用上述函数识别箭头键按下的示例:

  1. $input = waitForInput();
  2. switch($input){
  3. case chr(27).chr(91).chr(65):
  4. print 'Up Arrow';
  5. break;
  6. case chr(27).chr(91).chr(66):
  7. print 'Down Arrow';
  8. break;
  9. case chr(27).chr(91).chr(68):
  10. print 'Left Arrow';
  11. break;
  12. case chr(27).chr(91).chr(67):
  13. print 'Right Arrow';
  14. break;
  15. default:
  16. print 'Char: '.$input;
  17. break;
  18. }
展开查看全部

相关问题