【问题标题】:Non-blocking on STDIN in PHP CLIPHP CLI 中的 STDIN 非阻塞
【发布时间】:2012-03-10 11:59:43
【问题描述】:

是否可以使用非阻塞的 PHP 从STDIN 读取:

我试过了:

stream_set_blocking(STDIN, false);
echo fread(STDIN, 1);

还有这个:

$stdin = fopen('php://stdin', 'r');
stream_set_blocking($stdin, false);
echo 'Press enter to force run command...' . PHP_EOL;
echo fread($stdin, 1);

但它仍然会阻塞直到fread 获得一些数据。

我注意到一些关于此问题的公开错误报告(7 年前),所以如果无法完成,是否有人知道可以完成此任务的任何粗略的 hack(在 Windows 和 Linux 上)?

【问题讨论】:

  • 不确定我是否理解非阻塞行为应该是什么。如果没有等待输入标准输入的内容,fread() 是否应该返回 FALSE?它将如何与 EOF 区分开来?在我看来,您需要某种测试其他而不是fread() 来确定标准输入上是否有等待数据,因为没有描述性失败。
  • @Graham fread 不会返回,即暂停脚本的执行,直到向STDIN 提供输入。基本上我想要的是检查是否有任何用户输入STDIN,如果没有,请继续,或者运行其他一些东西。
  • @Graham“非阻塞”意味着fread() 应该立即返回,即使它无法读取数据量,它应该读取(第二个参数),但少于这个.在这种情况下,它应该返回一个空字符串。 @Petah 你试过fopen('php://stdin) 吗?我依稀记得,我之前遇到过STDIN 的问题。
  • @KingCrunch,刚刚尝试过,(根据我更新的问题),但结果相同。
  • 所以...stream_set_blocking(STDIN, false) 应该发生的行为是 fread() 从不等待,而 if (strlen(fread($stdin,1)) == 0),那么没有任何东西等待输入?

标签: stdin php


【解决方案1】:

这是我能想到的。它在 Linux 中运行良好,但在 Windows 上,只要我按下一个键,输入就会被缓冲,直到按下回车键。我不知道如何禁用流缓冲。

<?php

function non_block_read($fd, &$data) {
    $read = array($fd);
    $write = array();
    $except = array();
    $result = stream_select($read, $write, $except, 0);
    if($result === false) throw new Exception('stream_select failed');
    if($result === 0) return false;
    $data = stream_get_line($fd, 1);
    return true;
}

while(1) {
    $x = "";
    if(non_block_read(STDIN, $x)) {
        echo "Input: " . $x . "\n";
        // handle your input here
    } else {
        echo ".";
        // perform your processing here
    }
}

?>

【讨论】:

  • 谢谢,这适用于我的情况。但是,如果您可以使其与(输入键的东西)保持一致,那就更好了。如果没有更好的答案,我会奖励你。
  • 我将它与我的脚本集成在一起,它工作得很好,除了它必须等待输入。每次我 alt+tab 到窗口时,它都会暂停并等待输入。
  • 刚刚在这里遇到了这个问题,如果您使用的是 stream_set_read_buffer(STDIN, 0); 它可能会起作用并且,正如您已经提到的,stream_set_blocking(STDIN, false); - 第一个应该在读取时将 stdin 设置为无缓冲,第二个设置为非阻塞,这与无缓冲有点不同。
  • 这对我不起作用...它让我一直要求输入以继续。
  • 在 windows 上并不完美,请参阅:stackoverflow.com/questions/21464457/…
【解决方案2】:
system('stty cbreak');
while(true){
    if($char = fread(STDIN, 1)) {
        echo chr(8) . mb_strtoupper($char);
    }
}

【讨论】:

  • 我不知道为什么会这样,但它绝对对我有用! (ubuntu linux 18.04 gnu bash 4.4) 谢谢
  • 这个方案需要结合Martin的方案。否则,它在“等待字符”时不会有“处理循环”。我的意思是:这立即起作用,但只响应按键。 Martin's 允许在未按下任何键的情况下进行处理。 stty 技巧的要点。
【解决方案3】:

请注意,非阻塞 STDIN 正在工作。

【讨论】:

    【解决方案4】:

    Petah,我无法直接在 PHP 方面提供帮助,但我可以向您推荐我不久前看到的一篇文章,其中有人通过在 shell 脚本中测试是否存在待处理的数据来模拟晶体管命名管道。这是一本引人入胜的读物,并将 shell 脚本提升到了一个全新的极客水平。 :-)

    文章在这里:http://www.linusakesson.net/programming/pipelogic/

    所以......为了回答你的“粗略的黑客”请求,我想你可以通过命名管道来分流你的 stdio,然后 exec() 工具,其源包含在上面的 URL 中,以测试是否有任何东西正在等待通过管道发送。你可能想开发一些包装函数来帮助处理一些事情。

    我怀疑 pipelogic 解决方案仅适用于 Linux,或者至少需要类似 unix 的操作系统。不知道如何在 Windows 上实现这一点。

    【讨论】:

    • 抱歉,我真的不明白这对我的问题有什么帮助。
    • @Petah - 从管道读取的非阻塞 fread() 可以被一个函数复制,该函数仅在检查管道上是否有任何数据等待后运行阻塞 fread()。我发布的链接中的工具可让您测试该条件。我很确定这种方法在 Windows 中不起作用,因为缺少命名管道,所以我不会进一步工作,因为 Windows 兼容性是您问题的一个条件。
    猜你喜欢
    • 1970-01-01
    • 2016-08-05
    • 1970-01-01
    • 2011-07-14
    • 1970-01-01
    • 2015-07-24
    • 1970-01-01
    • 1970-01-01
    • 2013-03-13
    相关资源
    最近更新 更多