Idiomatic way to implement standard Unix behaviour of using STDIN if no files are specified on the command line?

I would probably go for a multi sub MAIN, something like:

multi sub MAIN(Bool :$debug)
{
    process-input($*IN.slurp);
}

multi sub MAIN(*@opt-files, Bool :$debug)
{
    process-input($_.IO.slurp) for @opt-files;
}

I'd probably do two things to change this. I'd break up the ?? !! onto different lines, and I'd go for a full method chain:

sub MAIN(*@opt-files, Bool :$debug, ... other named options ...) {
    my $input = @opt-files 
                  ?? @opt-files».IO».slurp.join
                  !! $*IN.slurp;

    ... process $input ...
}

You can also map it by using @opt-files.map(*.IO.slurp).join

Edit: building on ugexe's answer, you could do

sub MAIN(*@opt-files, Bool :$debug, ... other named options ...) {

    # Default to $*IN if not files
    @opt-files ||= '-';

    my $input = @opt-files».IO».slurp.join

    ... process $input ...

}

Something that I might expect to work is to set @*ARGS to the list of file names in the signature.
And then just use $*ARGFILES.

sub MAIN( *@*ARGS, Bool :$debug, ... other named options ...) {

    my $input = slurp; # implicitly calls $*ARGFILES.slurp()

    ... process $input ...
}

It doesn't work though.


You could get Rakudo to update $*ARGFILES by nulling it with a low-level null before you use it.

sub MAIN( *@*ARGS, Bool :$debug, ... other named options ...) {

    { use nqp; $*ARGFILES := nqp::null }

    my $input = slurp;

    ... process $input ...
}

But that is using an implementation detail that may change in the future.


A better way is to just directly create a new instance of IO::ArgFiles yourself.

You can even store it in $*ARGFILES. Then slurp on its own would slurp in all of the file contents.

sub MAIN( *@opt-files, Bool :$debug, ... other named options ...) {

    my $*ARGFILES = IO::ArgFiles.new( @opt-files || $*IN );

    my $input = slurp;

    ... process $input ...
}

Note that IO::ArgFiles is just an empty subclass of IO::CatHandle. So you could write IO::CatHandle.new( @opt‑files || $*IN ) instead.

Tags:

Raku