Como posso testar STDIN sem bloquear em Perl?
Pergunta
Estou escrevendo meu primeiro aplicativo Perl - um bot AOL Instant Messenger que se comunica com um microcontrolador Arduino, que por sua vez controla um servo que apertará o botão liga / desliga no servidor do nosso administrador de sistema, que congela aleatoriamente a cada 28 horas ou mais.
Já fiz todas as coisas difíceis, só estou tentando adicionar um último trecho de código para quebrar o loop principal e sair do AIM quando o usuário digitar 'sair'.
O problema é que, se eu tentar ler STDIN no loop principal do programa, ele bloqueará o processo até que a entrada seja inserida, essencialmente tornando o bot inativo.Eu tentei testar o EOF antes de ler, mas sem dados ...EOF sempre retorna falso.
Aqui está um exemplo de código com o qual estou trabalhando:
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";
Solução
O Perl integrado é select()
, que é uma passagem para o select()
chamada de sistema, mas para pessoas sãs eu recomendo IO::Select
.
Exemplo de código:
#!/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";
}
}
Outras dicas
eu achei aquilo OI::Selecionar funciona bem desde que STDOUT seja fechado, como quando o processo upstream no pipeline termina ou a entrada é de um arquivo.No entanto, se a saída estiver em andamento (como de "tail -f"), quaisquer dados parciais armazenados em buffer por <STDIN>
não será exibido.Em vez disso, use o sem buffer leitura de sistema:
#!/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);
}