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;

Tags:

Raku