在Perl中,如何在输入到达时立即处理输入,而不是等待换行?

时间:2022-05-31 12:17:24

I'd like to run a subcommand from Perl (or pipe it into a Perl script) and have the script process the command's output immediately, rather than waiting for a timeout, a newline, or a certain number of blocks. For example, let's say I want to surround each chunk of input with square brackets. When I run the script like this:

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

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

I'd like the output to be this, with each line appearing five seconds after the previous one:

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

[foo]
[bar]
[baz]

How do I do that?

我怎么做?

This works, but is really ugly:

这有效,但真的很难看:

#! /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";
}

Is there a more elegant way to do it?

有更优雅的方式吗?

5 个解决方案

#1


9  

From perlfaq5: How can I read a single character from a file? From the keyboard?. You probably also want to read How can I tell whether there's a character waiting on a filehandle?. Poll the filehandle. If there is a character there, read it and reset a timer. If there is not character there, try again. If you've retried and passed a certain time, process the input.

从perlfaq5:如何从文件中读取单个字符?从键盘?您可能还想阅读如何判断文件句柄中是否有字符等待?轮询文件句柄。如果那里有一个字符,请阅读它并重置一个计时器。如果那里没有角色,请再试一次。如果您已重试并经过一段时间,请处理输入。

After you read the characters, it's up to you to decide what to do with them. With all the flexibility of reading single characters comes the extra work of handling them.

阅读完字符后,您可以自行决定如何处理这些字符。阅读单个字符的所有灵活性带来了处理它们的额外工作。

#2


4  

Term::ReadKey can do this for you. In particular setting the ReadKey() mode to do the polling for you.

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

use Term::ReadKey;

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

#3


1  

If there's time inbetween each character, you might be able to detect the pauses.

如果每个角色之间有时间,您可能能够检测到暂停。

Perl also does line input - if you don't use getc you should be able to add newlines to the end of foo, bar, etc and perl will give you each line.

Perl也做行输入 - 如果你不使用getc,你应该能够在foo,bar等的末尾添加换行符,perl会给你每一行。

If you can't add newlines, and you can't depend on a pause, then what exactly do you expect the system to do to tell perl that it's started a new command? As far as perl is concerned, there's a stdin pipe, it's eating data from it, and there's nothing in the stdin pipe to tell you when you are executing a new command.

如果你不能添加换行符,并且你不能依赖暂停,那么你期望系统究竟告诉perl它是否已经启动了一个新命令?就perl而言,有一个stdin管道,它正在从中获取数据,并且stdin管道中没有任何内容可以告诉你何时执行新命令。

You might consider the following instead:

您可以考虑以下内容:

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

or

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

And modify your perl program to parse the input "command line" and execute each task, eating the stdout as needed.

并修改你的perl程序来解析输入“命令行”并执行每个任务,根据需要吃stdout。

-Adam

#4


0  

See How to change Open2 input buffering. (Basically, you have to make the other program think it's talking to a tty.)

请参见如何更改Open2输入缓冲。 (基本上,你必须让其他程序认为它与tty交谈。)

#5


0  

You didn't mention how you are reading input in your Perl script, but you might want to look at the getc function:

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

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

#1


9  

From perlfaq5: How can I read a single character from a file? From the keyboard?. You probably also want to read How can I tell whether there's a character waiting on a filehandle?. Poll the filehandle. If there is a character there, read it and reset a timer. If there is not character there, try again. If you've retried and passed a certain time, process the input.

从perlfaq5:如何从文件中读取单个字符?从键盘?您可能还想阅读如何判断文件句柄中是否有字符等待?轮询文件句柄。如果那里有一个字符,请阅读它并重置一个计时器。如果那里没有角色,请再试一次。如果您已重试并经过一段时间,请处理输入。

After you read the characters, it's up to you to decide what to do with them. With all the flexibility of reading single characters comes the extra work of handling them.

阅读完字符后,您可以自行决定如何处理这些字符。阅读单个字符的所有灵活性带来了处理它们的额外工作。

#2


4  

Term::ReadKey can do this for you. In particular setting the ReadKey() mode to do the polling for you.

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

use Term::ReadKey;

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

#3


1  

If there's time inbetween each character, you might be able to detect the pauses.

如果每个角色之间有时间,您可能能够检测到暂停。

Perl also does line input - if you don't use getc you should be able to add newlines to the end of foo, bar, etc and perl will give you each line.

Perl也做行输入 - 如果你不使用getc,你应该能够在foo,bar等的末尾添加换行符,perl会给你每一行。

If you can't add newlines, and you can't depend on a pause, then what exactly do you expect the system to do to tell perl that it's started a new command? As far as perl is concerned, there's a stdin pipe, it's eating data from it, and there's nothing in the stdin pipe to tell you when you are executing a new command.

如果你不能添加换行符,并且你不能依赖暂停,那么你期望系统究竟告诉perl它是否已经启动了一个新命令?就perl而言,有一个stdin管道,它正在从中获取数据,并且stdin管道中没有任何内容可以告诉你何时执行新命令。

You might consider the following instead:

您可以考虑以下内容:

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

or

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

And modify your perl program to parse the input "command line" and execute each task, eating the stdout as needed.

并修改你的perl程序来解析输入“命令行”并执行每个任务,根据需要吃stdout。

-Adam

#4


0  

See How to change Open2 input buffering. (Basically, you have to make the other program think it's talking to a tty.)

请参见如何更改Open2输入缓冲。 (基本上,你必须让其他程序认为它与tty交谈。)

#5


0  

You didn't mention how you are reading input in your Perl script, but you might want to look at the getc function:

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

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