Come posso testare STDIN senza bloccare in Perl?
Domanda
Sto scrivendo la mia prima app Perl: un bot AOL Instant Messenger che comunica con un microcontrollore Arduino, che a sua volta controlla un servo che premerà il pulsante di accensione sul server del nostro amministratore di sistema, che si blocca casualmente ogni 28 ore circa.
Ho portato a termine tutte le cose difficili, sto solo cercando di aggiungere un ultimo bit di codice per interrompere il ciclo principale e disconnettermi da AIM quando l'utente digita "esci".
Il problema è che, se provo a leggere da STDIN nel ciclo principale del programma, blocca il processo finché non viene immesso l'input, rendendo sostanzialmente inattivo il bot.Ho provato a testare EOF prima di leggere, ma nessun dado...EOF restituisce sempre false.
Di seguito è riportato un codice di esempio con cui sto lavorando:
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";
Soluzione
Il Perl integrato è select()
, che è un pass-through alla IO::Select
chiamata di sistema, ma per le persone sane consiglio <=> .
Esempio di codice:
#!/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";
}
}
Altri suggerimenti
Ho scoperto che IO :: Select funziona bene fino a quando STDOUT viene chiuso, ad esempio quando termina il processo a monte nella pipeline o l'input proviene da un file. Tuttavia, se l'output è in corso (ad esempio da & Quot; tail -f & Quot;), i dati parziali bufferizzati da <STDIN>
non verranno visualizzati. Utilizza invece il sysread senza buffer :
#!/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);
}