Wie kann ich STDIN ohne Blockierung in Perl testen?
Frage
Ich schreibe meine erste Perl-App – einen AOL Instant Messenger-Bot, der mit einem Arduino-Mikrocontroller kommuniziert, der wiederum einen Servo steuert, der den Netzschalter auf dem Server unseres Systemadministrators drückt, der etwa alle 28 Stunden zufällig einfriert.
Ich habe alle schwierigen Aufgaben erledigt. Ich versuche nur noch ein letztes Stück Code hinzuzufügen, um die Hauptschleife zu durchbrechen und mich von AIM abzumelden, wenn der Benutzer „Beenden“ eingibt.
Das Problem ist, wenn ich versuche, in der Hauptprogrammschleife von STDIN zu lesen, blockiert es den Prozess, bis eine Eingabe erfolgt, wodurch der Bot im Wesentlichen inaktiv wird.Ich habe vor dem Lesen versucht, EOF zu testen, aber keine Würfel ...EOF gibt einfach immer false zurück.
Hier ist ein Beispielcode, mit dem ich arbeite:
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";
Lösung
Das integrierte Perl ist select()
, was ein Durchgang zum ist select()
Systemaufruf, aber für vernünftige Leute empfehle ich IO::Select
.
Codebeispiel:
#!/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";
}
}
Andere Tipps
ich habe das gefunden IO::Auswählen Funktioniert einwandfrei, solange STDOUT geschlossen wird, z. B. wenn der Upstream-Prozess in der Pipeline beendet wird oder die Eingabe aus einer Datei erfolgt.Wenn die Ausgabe jedoch fortlaufend ist (z. B. von „tail -f“), werden alle Teildaten von gepuffert <STDIN>
wird nicht angezeigt.Verwenden Sie stattdessen die ungepufferte Version 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);
}