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 正在缓冲输入。
最终我希望能够读取按键,例如向上箭头、向下箭头等。
I want to read a single character at-a-time from the command line in PHP, however it seems as though there is some kind of input buffering from somewhere preventing this.
Consider this code:
#!/usr/bin/php
<?php
echo "input# ";
while ($c = fread(STDIN, 1)) {
echo "Read from STDIN: " . $c . "\ninput# ";
}
?>
Typing in "foo" as the input (and pressing enter), the output I am getting is:
input# foo
Read from STDIN: f
input# Read from STDIN: o
input# Read from STDIN: o
input# Read from STDIN:
input#
The output I am expecting is:
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#
(That is, with characters being read and processed as they are typed).
However, currently, each character is being read only after enter is pressed. I have a suspicion the TTY is buffering the input.
Ultimately I want to be able to read keypresses such as UP arrow, DOWN arrow, etc.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(6)
我的解决方案是在 TTY 上设置
-icanon
模式(使用stty
)。例如:所以,现在有效的代码是:
输出:
此处给出的答案的道具:
有没有办法从(远程)终端会话等待并获取按键?
有关详细信息,请参阅:
http://www.faqs.org/docs/Linux -HOWTO/Serial-Programming-HOWTO.html#AEN92
完成后不要忘记恢复 TTY...
恢复 tty 配置
将终端重置回可以通过在更改 tty 状态之前保存它来完成此操作。完成后您可以恢复到该状态。
例如:
这是保留 tty 并将其恢复为用户在开始之前所拥有的状态的唯一方法。
请注意,如果您不担心保留原始状态,则只需执行以下操作即可将其重置回默认的“正常”配置:
The solution for me was to set
-icanon
mode on the TTY (usingstty
). Eg.:So, the the code that now works is:
Output:
Props to the answer given here:
Is there a way to wait for and get a key press from a (remote) terminal session?
For more information, see:
http://www.faqs.org/docs/Linux-HOWTO/Serial-Programming-HOWTO.html#AEN92
Don't forget to restore the TTY when you're done with it...
Restoring the tty configuration
Resetting the terminal back to the way it was can be done by saving the tty state before you make changes to it. You can then restore to that state when you're done.
For example:
This is the only way to preserve the tty and put it back how the user had it before you began.
Note that if you're not worried about preserving the original state, you can reset it back to a default "sane" configuration simply by doing:
这是一种适用于我的 readline 和流函数的方法,而不需要弄乱 tty 的东西。
在 OSX 上使用 PHP 5.5.8 进行了测试。
Here is a way that works for me with readline and stream functions, without needing to mess with tty stuff.
Tested with PHP 5.5.8 on OSX.
下面的函数是 @seb 答案的简化版本,可用于捕获单个字符。它不需要
stream_select
,并使用readline_callback_handler_install
固有的阻塞,而不是创建 while 循环。它还删除处理程序以允许正常进一步输入(例如 readline)。The function below is a simplified version of @seb's answer that can be used to capture a single character. It does not require
stream_select
, and usesreadline_callback_handler_install
's inherent blocking rather than creating a while loop. It also removes the handler to allow further input as normal (such as readline).以下函数将等待用户输入字符,然后立即返回。此方法支持多字节字符,因此也适用于检测箭头键按下。
下面是使用上述函数来识别箭头键按下的示例:
The following function will wait until the user enters a character and then returns it immediately. This approach supports multibyte characters so will also work for detecting arrow key presses.
Here is an example of using the above function to identify an arrow key press:
如果您不在 Windows powershell/cmd 中,请使用
stream_set_blocking()
,正如 joeldg 已经说过的那样。我正在使用 WSL (Ubuntu) 并且它有效:在我的代码中我使用 < code>fgets() 在每个循环中读取所有行,而不是一个接一个地读取字符。因此,如果用户在第二秒之间输入 abracadabra,则循环不会迭代每个字符。
If you're not in Windows powershell/cmd, use
stream_set_blocking()
, as joeldg already said. I'm use WSL (Ubuntu) and it's worked:In my code I use
fgets()
to read all line in every loop rather than characters one by one. So if user type abracadabra between second, then loop wouldn't iterate every character.