Я пишу свое первое приложение на Perl - бот AOL Instant Messenger, который общается с микроконтроллером Arduino, который, в свою очередь, управляет сервоприводом, который нажимает кнопку питания на сервере нашего системного администратора, который зависает случайным образом каждые 28 часов или около того.
Я выполнил все сложные задачи, я просто пытаюсь добавить последний кусочек кода, чтобы разорвать основной цикл и выйти из AIM, когда пользователь нажимает «quit».
Проблема в том, что, если я пытаюсь читать из 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";
Встроенный 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 работает нормально до тех пор, пока 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);
}
IO::Select
работает только в UNIX, не работает в Windows!IO::Select
иselect()
вообще на Windows работает для (некоторых) сокетов интернет-домена, но только это. Windows является поразительно сломанной платформой, поэтому не ожидайте, что такой простой кроссплатформенный API будет работать во всех случаях на ней. Вы должны сделать одно для сокетов, другое для файлов / именованных каналов, другое для анонимных каналов / дескрипторов «консоли» и т. Д. Тот факт, что они вызывали ихpoll()
WPAPoll
, заставлял их работать только для интернет-сокетов и отказывался фиксировать известные ошибки в нем, потому что «poll () устарел» должно быть предупреждением, чтобы держаться подальше .