如何在 Perl 中测试 STDIN 而不会阻塞?
Posted
技术标签:
【中文标题】如何在 Perl 中测试 STDIN 而不会阻塞?【英文标题】:How can I test STDIN without blocking in Perl? 【发布时间】:2010-09-06 01:26:50 【问题描述】:我正在编写我的第一个 Perl 应用程序——一个与 Arduino 微控制器对话的 AOL Instant Messenger 机器人,后者反过来控制一个伺服系统,该伺服系统将按下我们系统管理员服务器上的电源按钮,每 28 小时左右随机冻结一次.
我已经完成了所有困难的事情,我只是想添加最后一点代码来打破主循环并在用户键入“退出”时退出 AIM。
问题是,如果我尝试在主程序循环中从 STDIN 读取数据,它会阻止进程直到输入输入,从而使机器人处于非活动状态。我在阅读之前尝试过测试 EOF,但没有骰子...... EOF 总是返回 false。
下面是我正在使用的一些示例代码:
while(1)
$oscar->do_one_loop();
# Poll to see if any arduino data is coming in over serial port
my $char = $port->lookfor();
# If we get data from arduino, then print it
if ($char)
print "" . $char ;
# reading STDIN blocks until input is received... AAARG!
my $a = <STDIN>;
print $a;
if($a eq "exit" || $a eq "quit" || $a eq 'c' || $a eq 'q') last;
print "Signing off... ";
$oscar->signoff();
print "Done\n";
print "Closing serial port... ";
$port->close() || warn "close failed";
print "Done\n";
【问题讨论】:
【参考方案1】:Perl 内置是select()
,它是select()
系统调用的传递,但对于理智的人,我推荐IO::Select
。
代码示例:
#!/usr/bin/perl
use IO::Select;
$s = IO::Select->new();
$s->add(\*STDIN);
while (++$i)
print "Hiya $i!\n";
sleep(5);
if ($s->can_read(.5))
chomp($foo = <STDIN>);
print "Got '$foo' from STDIN\n";
【讨论】:
请注意,IO::Select
仅适用于 UNIX,不适用于 Windows!
实际上 IO::Select
和 select()
通常在 Windows 上适用于(某些)互联网域套接字,但仅此而已。 Windows 是一个令人震惊的破碎平台,所以不要指望这样一个简单的跨平台 API 可以适用于它的所有情况。您必须为套接字做一件事,为文件/命名管道做另一件事,为匿名管道/“控制台”句柄做另一件事......事实上,他们称他们为poll()
WPAPoll
,只使它适用于互联网套接字,并拒绝修复其中的已知错误,因为“poll() 已弃用”应该是警告远离。【参考方案2】:
我发现IO::Select 只要 STDOUT 关闭就可以正常工作,例如当管道中的上游进程退出时,或者输入来自文件时。但是,如果输出正在进行(例如来自“tail -f”),则不会显示由<STDIN>
缓冲的任何部分数据。相反,使用无缓冲的sysread:
#!/usr/bin/perl
use IO::Select;
$s = IO::Select->new(\*STDIN);
while (++$i)
if ($s->can_read(2))
last unless defined($foo=get_unbuf_line());
print "Got '$foo'\n";
sub get_unbuf_line
my $line="";
while (sysread(STDIN, my $nextbyte, 1))
return $line if $nextbyte eq "\n";
$line .= $nextbyte;
return(undef);
【讨论】:
以上是关于如何在 Perl 中测试 STDIN 而不会阻塞?的主要内容,如果未能解决你的问题,请参考以下文章
Perl - Win32 - 如何从另一个进程非阻塞读取文件句柄?
如何在 perl 中为反引号加载 STDIN(不写入临时文件)