【问题标题】:In Perl, how do I process input as soon as it arrives, instead of waiting for newline?在 Perl 中,如何在输入到达后立即处理,而不是等待换行符?
【发布时间】:2010-09-17 20:28:23
【问题描述】:

我想从 Perl 运行一个子命令(或将其通过管道传输到 Perl 脚本中)并让脚本立即处理命令的输出,而不是等待超时、换行符或一定数量的块。例如,假设我想用方括号将每个输入块括起来。当我像这样运行脚本时:

$ ( echo -n foo ; sleep 5 ; echo -n bar ; sleep 5; echo baz) | my_script.pl

我希望输出是这样的,每行在前一行之后出现 5 秒:

[foo]
[bar]
[baz]

我该怎么做?

这可行,但真的很丑:

#! /usr/bin/perl -w

use strict;
use Fcntl;

my $flags = '';
fcntl(STDIN, F_GETFL, $flags);
$flags |= O_NONBLOCK;
fcntl(STDIN, F_SETFL, $flags);

my $rin = '';
vec($rin,fileno(STDIN),1) = 1;
my $rout;

while (1) {
  select($rout=$rin, undef, undef, undef);
  last if eof();

  my $buffer = '';

  while (my $c = getc()) {
    $buffer .= $c;
  }

  print "[$buffer]\n";
}

有更优雅的方法吗?

【问题讨论】:

    标签: perl


    【解决方案1】:

    来自 perlfaq5:How can I read a single character from a file? From the keyboard?。您可能还想阅读How can I tell whether there's a character waiting on a filehandle?。轮询文件句柄。如果那里有一个字符,读取它并重置一个计时器。如果那里没有字符,请再试一次。如果您已重试并经过了一定时间,请处理输入。

    阅读完这些字符后,您可以决定如何处理它们。读取单个字符的所有灵活性伴随着处理它们的额外工作。

    【讨论】:

    • 那么你是说我的例子是最优雅的方式吗?还是有更好的方法?
    • 我的意思是,如果你想要所有的控制权,你必须自己做。当你想玩那个级别的东西时,没有魔法小马:)
    【解决方案2】:

    Term::ReadKey 可以为您做到这一点。特别是设置 ReadKey() 模式为您进行轮询。

    use Term::ReadKey;
    
    $| = 1;
    while( my $key = ReadKey(10) ) {
        print $key;
    }
    

    【讨论】:

      【解决方案3】:

      如果每个字符之间有时间间隔,您也许可以检测到停顿。

      Perl 也进行行输入 - 如果您不使用 getc,您应该能够在 foo、bar 等的末尾添加换行符,perl 会为您提供每一行。

      如果您不能添加换行符,并且不能依赖暂停,那么您希望系统具体做什么来告诉 perl 它开始了一个新命令?就 perl 而言,有一个标准输入管道,它正在从中获取数据,当您执行新命令时,标准输入管道中没有任何内容可以告诉您。

      您可以考虑以下方法:

      $ echo "( echo -n foo ; sleep 5 ; echo -n bar ; sleep 5; echo baz)" | my_script.pl
      

      $ my_script.pl$ "echo -n foo ; sleep 5 ; echo -n bar ; sleep 5; echo baz"
      

      并修改您的 perl 程序以解析输入的“命令行”并执行每个任务,根据需要使用标准输出。

      -亚当

      【讨论】:

      • 我已经用一个有效的例子更新了这个问题,所以这绝对是可能的。现在我只想以正确的方式去做。
      【解决方案4】:

      How to change Open2 input buffering。 (基本上,您必须让其他程序认为它正在与 tty 对话。)

      【讨论】:

      • 这似乎没有必要——我已经用一些有效的示例代码更新了这个问题,没有欺骗其他程序认为它正在与 tty 对话。
      【解决方案5】:

      您没有提到如何在 Perl 脚本中读取输入,但您可能想查看 getc 函数:

      $|++; # set autoflush on output
      while ($c = getc(STDIN)) {
          print $c;
      }
      

      【讨论】:

      • 这不起作用——我已经更新了问题以解释原因。
      • 我的脚本没有输出任何方括号,那些必须来自您的脚本。如果没有看到脚本的其余部分,很难说您可能在做什么会导致这种情况。
      • 使用您的确切脚本,它一次打印一个字符,而不是一次打印一个块。为了说明,将打印行更改为打印“[$c]”;
      猜你喜欢
      • 1970-01-01
      • 2023-02-14
      • 2020-10-17
      • 2019-02-22
      • 2011-02-28
      • 1970-01-01
      • 2011-11-15
      • 1970-01-01
      • 2020-04-17
      相关资源
      最近更新 更多