How do I parse and validate command line arguments in Raku (formerly known as Perl 6)?
Basics
That feature is built into Raku (formerly known as Perl 6). Here is the equivalent of your Getopt::Long
code in Raku:
sub MAIN ( Str :$file = "file.dat"
, Num :$length = Num(24)
, Bool :$verbose = False
)
{
$file.say;
$length.say;
$verbose.say;
}
MAIN
is a special subroutine that automatically parses command line arguments based on its signature.
Str
and Num
provide string and numeric type constraints.
Bool
makes $verbose
a binary flag which is False
if absent or if called as --/verbose
. (The /
in --/foo
is a common Unix command line syntax for setting an argument to False
).
:
prepended to the variables in the subroutine signature makes them named (instead of positional) parameters.
Defaults are provided using $variable =
followed by the default value.
Aliases
If you want single character or other aliases, you can use the :f(:$foo)
syntax.
sub MAIN ( Str :f(:$file) = "file.dat"
, Num :l(:$length) = Num(24)
, Bool :v(:$verbose) = False
)
{
$file.say;
$length.say;
$verbose.say;
}
:x(:$smth)
makes additional alias for --smth
such as short alias -x
in this example. Multiple aliases and fully-named is available too, here is an example: :foo(:x(:bar(:y(:$baz))))
will get you --foo
, -x
, --bar
, -y
and --baz
and if any of them will pass to $baz
.
Positional arguments (and example)
MAIN
can also be used with positional arguments. For example, here is Guess the number (from Rosetta Code). It defaults to a min of 0 and max of 100, but any min and max number could be entered. Using is copy
allows the parameter to be changed within the subroutine:
#!/bin/env perl6
multi MAIN
#= Guessing game (defaults: min=0 and max=100)
{
MAIN(0, 100)
}
multi MAIN ( $max )
#= Guessing game (min defaults to 0)
{
MAIN(0, $max)
}
multi MAIN
#= Guessing game
( $min is copy #= minimum of range of numbers to guess
, $max is copy #= maximum of range of numbers to guess
)
{
#swap min and max if min is lower
if $min > $max { ($min, $max) = ($max, $min) }
say "Think of a number between $min and $max and I'll guess it!";
while $min <= $max {
my $guess = (($max + $min)/2).floor;
given lc prompt "My guess is $guess. Is your number higher, lower or equal (or quit)? (h/l/e/q)" {
when /^e/ { say "I knew it!"; exit }
when /^h/ { $min = $guess + 1 }
when /^l/ { $max = $guess }
when /^q/ { say "quiting"; exit }
default { say "WHAT!?!?!" }
}
}
say "How can your number be both higher and lower than $max?!?!?";
}
Usage message
Also, if your command line arguments don't match a MAIN
signature, you get a useful usage message, by default. Notice how subroutine and parameter comments starting with #=
are smartly incorporated into this usage message:
./guess --help
Usage:
./guess -- Guessing game (defaults: min=0 and max=100)
./guess <max> -- Guessing game (min defaults to 0)
./guess <min> <max> -- Guessing game
<min> minimum of range of numbers to guess
<max> maximum of range of numbers to guess
Here --help
isn't a defined command line parameter, thus triggering this usage message.
See also
See also the 2010, 2014, and 2018 Perl 6 advent calendar posts on MAIN
, the post Parsing command line arguments in Perl 6, and the section of Synopsis 6 about MAIN.
Alternatively, there is a Getopt::Long for perl6 too. Your program works in it with almost no modifications:
use Getopt::Long;
my $data = "file.dat";
my $length = 24;
my $verbose;
get-options("length=i" => $length, # numeric
"file=s" => $data, # string
"verbose" => $verbose); # flag
say $length;
say $data;
say $verbose;