Как я могу проверить STDIN без блокировки в Perl?

Я пишу свое первое приложение на 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";
18.08.2008 03:02:10
2 ОТВЕТА
РЕШЕНИЕ

Встроенный 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";
  }
}
19
15.07.2009 04:26:55
Большое примечание для всех остальных, IO::Selectработает только в UNIX, не работает в Windows!
slm 9.02.2012 17:41:54
На самом деле IO::Selectи select()вообще на Windows работает для (некоторых) сокетов интернет-домена, но только это. Windows является поразительно сломанной платформой, поэтому не ожидайте, что такой простой кроссплатформенный API будет работать во всех случаях на ней. Вы должны сделать одно для сокетов, другое для файлов / именованных каналов, другое для анонимных каналов / дескрипторов «консоли» и т. Д. Тот факт, что они вызывали их poll() WPAPoll, заставлял их работать только для интернет-сокетов и отказывался фиксировать известные ошибки в нем, потому что «poll () устарел» должно быть предупреждением, чтобы держаться подальше .
alexchandel 18.05.2016 04:38:03

Я обнаружил, что 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);
}
1
21.02.2019 21:35:56