Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How do I parse and validate command line arguments in Raku (formerly known as Perl 6)?

Tags:

raku

In Perl 5, I can use Getopt::Long to parse commandline arguments with some validation (see below from http://perldoc.perl.org/Getopt/Long.html).

use Getopt::Long;
my $data   = "file.dat";
my $length = 24;
my $verbose;
GetOptions ("length=i" => \$length,    # numeric
            "file=s"   => \$data,      # string
            "verbose"  => \$verbose)   # flag
or die("Error in command line arguments\n");

say $length;
say $data;
say $verbose;

Here =i in "length=i" creates a numeric type constraint on the value associated with --length and =s in "file=s" creates a similar string type constraint.

How do I do something similar in Raku (née Perl 6)?

like image 214
Christopher Bottoms Avatar asked Apr 17 '15 15:04

Christopher Bottoms


People also ask

How do I get command line arguments in Perl?

The @ARGV array holds the command line argument. There is no need to use variables even if you use "use strict". By default, this variable always exists and values from the command line are automatically placed inside this variable. To access your script's command-line arguments, you just need to read from @ARGV array.


2 Answers

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.

like image 138
Christopher Bottoms Avatar answered Oct 07 '22 14:10

Christopher Bottoms


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;
like image 36
Leon Timmermans Avatar answered Oct 07 '22 14:10

Leon Timmermans