Perl6: check if STDIN has data

2020-07-11 08:11发布

问题:

In my Perl 6 script, I want to do a (preferably non-blocking) check of standard input to see if data is available. If this is the case, then I want to process it, otherwise I want to do other stuff.

Example (consumer.p6):

#!/usr/bin/perl6
use v6.b;
use fatal;
sub MAIN() returns UInt:D {
    while !$*IN.eof {
        if some_fancy_check_for_STDIN() { #TODO: this needs to be done.
            for $*IN.lines -> $line {
                say "Process '$line'";
            }
        }
        say "Do something Else.";
    }
    say "I'm done.";
    return 0;
}

As a STDIN-Generator I wrote another Perl6 script (producer.p6):

#!/usr/bin/perl6
use v6.b;
use fatal;

sub MAIN() returns UInt:D {
    $*OUT.say("aaaa aaa");
    sleep-until now+2;

    $*OUT.say("nbbasdf");
    sleep-until now+2;

    $*OUT.say("xxxxx");
    sleep-until now+2;

    return 0;
}

If consumer.p6 works as expected, it should produce the following output, if called via ./producer.p6 | ./consumer.p6:

Process 'aaaa aaa'
Do something Else.
Process 'nbbasdf'
Do something Else.
Process 'xxxxx'
Do something Else.
I'm done.

But actually, it produces the following output (if the if condition is commented out):

Process 'aaaa aaa'
Process 'nbbasdf'
Process 'xxxxx'
Do something Else.
I'm done.

回答1:

You are using an old version of Perl 6, as v6.b is from before the official release of the language.
So some of what I have below may need a newer version to work.

Also why are you using sleep-until now+2 instead of sleep 2?


One way to do this is to turn the .lines into a Channel, then you can use .poll.

#!/usr/bin/env perl6
use v6.c;

sub MAIN () {
    # convert it into a Channel so we can poll it
    my $lines = $*IN.Supply.lines.Channel;

    my $running = True;
    $lines.closed.then: {$running = False}

    while $running {
        with $lines.poll() -> $line {
            say "Process '$line'";
        }
        say "Do something Else.";
        sleep ½;
    }
    say "I'm done.";
}

Note that the code above blocks at the my $lines = … line currently; so it doesn't start doing something until the first line comes in. To get around that you could do the following

my $lines = supply {
    # unblock the $*IN.Supply.lines call
    whenever start $*IN.Supply {
        whenever .lines { .emit }
    }
}.Channel;


标签: raku