How do I take multiple filenames from the Raku command line?

Viewed 186

This Raku program works as I expect:

sub MAIN($name) { say "Got $name" }

I can pass a single name on the command line:

$ raku m1.raku foo
Got foo

The obvious extension, however,

sub MAIN(@names) { say "Got $_" for @names }

doesn't work:

$ raku mm.raku foo
Usage:
  mm.raku <names>
$ raku mm.raku foo bar
Usage:
  mm.raku <names>

What am I doing wrong?

4 Answers

What @cjm said.

However, you can go a little further than that, checking whether the names you specified, are actually files. And produce an error message if they are not. The trick is to use multi-dispatch:

subset File of Str where *.IO.f;

multi sub MAIN(*@files where @files.all ~~ File) {
    say "These are all files: @files.join(",")";
}
multi sub MAIN(*@files) {
    say "These are *NOT* files: @files.grep(* !~~ File).join(",")";
}

The first candidate will be run if all the names specified on the command line are in fact files. The second candidate will be run if the first didn't fire, implying that not all names specified are in fact files.

You must use the slurpy array signature for this:

sub MAIN(*@names) { say "Got $_" for @names }

Works as desired:

$ raku mm.raku
$ raku mm.raku foo
Got foo
$ raku mm.raku foo bar
Got foo
Got bar

There is a special $*ARGFILES variable which provides a way to iterate over files passed in to the program on the command line.

#!/usr/bin/raku
    
for $*ARGFILES.handles -> $fh {

    say $fh;
}
$ ./enum_files.raku links.txt words.txt 
IO::Handle<"links.txt".IO>(opened)
IO::Handle<"words.txt".IO>(opened)

We can use its lines method to read lines of the given files.

#!/usr/bin/raku

.say for $*ARGFILES.lines
$ ./read_lines.raku words.txt words2.txt 
sky
cloud
cup
rock
war
tea
coffee
falcon
...

@cjm's answer is the proper one to your question. But just as Jan said there's another way:

say "Got:" @*ARGS

Where @*ARGS is a dynamic variable defined as the args on the command line.

$*ARGFILES has some extra nuance to it. Outside of main gets its values from @*ARGS or $*IN if there are no args. Inside main it just does $*IN.

Related