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;
So I'm guessing Zef adds it?
CompUnit::Repository::Installation adds it
Why is it there? One reason is because some wrapper needs to manage bin scripts the same name that would otherwise clash if they were in the same directory.
Is there some way to use the run-script method shown above without Zef?
run-script is for CompUnit::Repository::Installation only -- if you aren't installing a module then run-script won't be of interest
Assuming I do need to have my users download two files, where should I have them install the files?
Well the recommended/idiomatic way would be to use the core raku functionality to install things (i.e. use zef). Otherwise where you should put some code is either a) not going to matter or b) going to be mostly dependendant on your environment, what is idiomatic for your os, etc.
Does it need to be copied into their Raku module search path (and, if so, what command would show them what that path is)
echo $RAKULIB should suffice for showing the module search path in most cases, especially if they aren't interested in where the installation paths are. And as such you can instruct users to set e.g. RAKULIB=$FROB_LIB_DIR to point wherever your library is if you want them to be able to run your script without manually specifying it via raku -I../ frobnicate (so they don't copy the code anywhere special, they just point to wherever they e.g. clone your repo). Ditto for working with $PATH.
Or can I modify the frobnicate wrapper in some way (maybe by changing raku to raku -Ilib or something like that?) in a way that would let them install both to directory on their PATH?
I would advise against installing things based on some value in $PATH. Instruct users to set $PATH, don't install things to $PATH.
Technically you could add use lib '../' to your script, but using use lib in a script that you also want users to install normally is less than ideal since its adding an unused, potentially hijackable module search path when being run from such an install.
If you want your code to precompile then I suggest putting it in a module, and instructing your users that, if they don't intent to install it, to invoke it via raku -I../ ./frobnicate on a per-use basis or something like export RAKULIB="$FROB_LIB_DIR,$RAKULIB" followed by ./frobnicate for something more permanent. Alternatively if someone evenetually implements precompilation of scripts then you could just use the single file approach.
Some comments on this topic.
It is optimal, I think, to use
zefbecausezefwill also load dependencies. It is already unusual to be able to write a Raku program without using other modules and I would expect this to become even more unusual as more Raku modules get developed.I forget which modules I have installed on my system, and included in a program. By specifying everything in
META6.json, then runningzef test ., the chance of ensuring someone else can download the module is improved. Actually, I have found the best way to ensure this is to create a docker file and try to install a new module in a docker image/container - but that's another topic.I have found (for Linux, and I cannot comment about Windows) that if I:
- write an executable in Raku (see below), lets call it
MyWonder, - place it in the
<distribution>/binfolder, - give it +x permission(s), and
- specify it in the distribution's root
META6.jsonfile - (publish the distribution)
then when
zefinstalls the distribution,MyWonderwill work on the command line (assuming thatzefitself works on the command line, implying thatPATHcontains the directory to zef).- write an executable in Raku (see below), lets call it
The optimal (for me) way of invoking a program that is meant to be used on command line, or invoked by a desktop, is:
- put the following into the
MyWonderfile (no need for extension)
use v6.d; use MyWonderLife;- put all of the functionality into
MyWonderLife.rakumod - make sure there is at least one
sub MAINinMyWonderLife.rakumod - specify
MyWonderLifein theMETA6.jsonfile (which allows a lot of flexibility about which directory you can put the actualMyWonderLife.rakummodfile under; it doesn't have to belib/) - create a simple test
t/basic.rakutestfor the distribution, with just the testuse-ok 'MyWonderLife;
This recipe means that all the functionality is precompiled by zef, so that when the executable is called by the user, there is a much quicker response. The slowest part when using Raku is compiling a program. By installing with zef, this is done once during installation. Programs in all languages are slow to install, so a Raku program does not cause a user to wonder what is happening at the moment it is being used.
Secondly, it is possible to use several
multi sub MAINto handle a variety of calling situations, and to use the range of command line options that can now be handled. Whilst this is obviously possible in any script, putting them all in a.rakumodfile seems (to me) to be more natural.I've found that extensive tests are a pain to watch when modules are being installed, so I have begun to move most of the development and maintenance testing to
xt/and only have simple installation tests int/.Finally, with this recipe (and assuming you have given the distribution the name MyWonderLife in the
META6.jsonfile, the installation instructions forMyWonder, assuming it is possible to call the program with no arguments are simply:Use zef to install MyWonderLife, eg.,
zef install MyWonderLifeand use it on a Command Line as follows:
MyWonder- put the following into the