Camelia

Parsing command line arguments in Perl 6

When writing a program, one of the best ways to make it reusable is by accepting parameters on the command line.

For example if you are writing a program that needs to parse a log file, you might want to supply the name of the logfile on the command line.

If the application needs to send an e-mail report, it is more generic if the user can supply the address as a command line parameter: --to boss@company

How can you let your Perl 6 application to accept command line arguments?

Note! This site is about Perl 6, the future version of Perl.
If you are looking for a solution for the current production version of Perl 5, please check out the Perl 5 tutorial.

When Perl 6 is launched it fills the system variable @*ARGS with all the values from the command line. A simple script like the following can demonstrate what we get:

use v6;

say @*ARGS.perl;

Save as cli.pl6 and run

perl6 cli.pl6 first --second "third and fourth"

The output will be

Array.new("first", "--second", "third and fourth")

The @*ARGS in Perl 6 is the same as @ARGV in Perl 5. It does NOT contain the name of the executable itself as it is done in C and Python for example.

We could now write some code that goes over the values in @*ARGS, but luckily Perl 6 provides a built-in mechanism for that. It is the same mechanism that works for Perl 6 subroutines. After all the command line parameters of a program are very similar to the arguments of a function.

MAIN

There is a special name in Perl 6 called MAIN. If your main program file contains a subroutine called MAIN, that subroutine will be executed when the program is launched.

Furthermore, the signature of that subroutine is the expected list of arguments of the whole program.

Let's see a simple example:

use v6;

sub MAIN($source) {
  say "source: $source";
}

Save it as cli.pl6 and run it as perl6 cli.pl6.

You will get:

Usage:
  cli.pl6 <source>

That's because the MAIN subroutine expects a single positional argument that will be assigned t the $source variable.

Now run perl6 cli.pl6 input.txt.

The output will be:

source: input.txt

That means, the command line parameter was accepted and the MAIN sub was called with $source having the value "input.txt".

Boolean argument with a default value

In the next example, we extended the signature. In addition to the $source field we are now expecting a boolean value to be assigned to $debug. We also gave it a default value, making this parameter optional.

use v6;

sub MAIN($source, Bool $debug = False) {
  say "source: $source";
  say "debug:  $debug";
}

Save the code and run it as perl6 cli.pl6.

You will find the output quite clear. We have to supply the source parameter and optionally the debug parameter.

Usage:
  cli.pl6 <source> [<debug>]

Let's try it again running it with one parameter: perl6 cli.pl6 data.txt

We get this output:

source: data.txt
debug:  False

And try it again, now supplying both parameters: perl6 cli.pl6 data.txt True

source: data.txt
debug:  True

You cannot pass the values in the opposite order and the boolean values must be True or False.

If we try something else: perl6 cli.pl6 data.txt 1 we get the usage message again:

Usage:
  cli.pl6 <source> [<debug>]

Named parameter

As in regular subroutines, Perl 6 allows to turn arguments into named parameter.

Put a colon : in front of the variable name to turn it into a named variable:

use v6;

sub MAIN($source, Bool :$debug = False) {
  say "source: $source";
  say "debug:  $debug";
}

Let's see what happens if we execute the code without any parameter: perl6 cli.pl6

The usage message indicated that now we need to used the --debug flag if we want to turn on debugging and that the named parameters must come before the positional parameters.

Usage:
  cli.pl6 [--debug] <source>

Try this: perl6 cli.pl6 data.txt

source: data.txt
debug:  False

And now try perl6 cli.pl6 --debug data.txt

source: data.txt
debug:  True

You cannot change the order of the parameters as positional parameters have to arrive after the named parameters. try perl6 cli.pl6 data.txt --debug and you'll get the usage message.

All the parameters are named

In the next example we turn the source parameter to be named as well by preceding it with a colon ::

use v6;

sub MAIN(:$source, Bool :$debug = False) {
  say "source: $source";
  say "debug:  $debug";
}

Try without any parameters: perl6 cli.pl6 and get:

use of uninitialized value of type Any in string context  in method Str at ...

source:
debug:  True

That happens because named parameters are optional and the code will run without any parameter. When we try to print the content of the $source variable, it will be undef and generate this warning.

There are several ways to fix this:

We could set a default value, even if that is an empty string:

sub MAIN(:$source = '', Bool :$debug = False) {

Required parameters

Exclamation point

We could also tell Perl, that $source is a required parameter by adding a trailing exclamation point !.

sub MAIN(:$source!, Bool :$debug = False) {

Try to run the program now without any parameter perl6 cli.pl6

and you'll get the new usage message.

Usage:
  code\cli.pl6 --source=<Any> [--debug]

You could, of course, declare that the $source variable should be an integer Int or some other data type, if that was the requirement. For example like this:

sub MAIN(Int :$source!, Bool :$debug = False) {

In that case the usage message will indicate the required data type:

Usage:
  code\cli.pl6 --source=<Int> [--debug]

In order to supply the value for the source, currently you have to use exactly the above form

perl6 cli.pl6 --source=data.txt

source: data.txt
debug:  False

If you happen to try the other common way:

perl6 cli.pl6 --source data.txt

You will get the usage message as Rakudo currently does not yet support this form.

Conclusion

There is more of course, but I think this can already get you started writing applications Perl 6 that accept parameters on the command line.


The Perl 6 Tricks and Treats newsletter has been around for a while. If you are interested to get special notification when there is new content on this site, it is the best way to keep track:
Email:
Full name:
This is a newsletter temporarily running on my personal site (szabgab.com) using Mailman, till I implement an alternative system in Perl 6.
Gabor Szabo
Written by Gabor Szabo

Published on 2012-09-14



Comments

In the comments, please wrap your code snippets within <pre> </pre> tags and use spaces for indentation.
comments powered by Disqus

Perl 6 Tricks and Treats newsletter

Register to the free newsletter now, and get updates and news.
Email:
Name: