Command Line Subcommands in Perl

Here’s the translation of the Go code example to Perl, formatted in Markdown suitable for Hugo:

Our first program demonstrates how to create command-line subcommands with their own set of flags. This is similar to how tools like git have subcommands like git commit and git push, each with their own options.

#!/usr/bin/env perl

use strict;
use warnings;
use Getopt::Long qw(GetOptionsFromArray);
use Pod::Usage;

sub main {
    # We declare subcommands and their specific options
    my %commands = (
        'foo' => \&cmd_foo,
        'bar' => \&cmd_bar,
    );

    # The subcommand is expected as the first argument to the program
    if (@ARGV < 1) {
        print "expected 'foo' or 'bar' subcommands\n";
        exit 1;
    }

    my $command = shift @ARGV;

    # Check which subcommand is invoked
    if (exists $commands{$command}) {
        $commands{$command}->();
    } else {
        print "expected 'foo' or 'bar' subcommands\n";
        exit 1;
    }
}

# For every subcommand, we parse its own flags and
# have access to trailing positional arguments
sub cmd_foo {
    my $enable = 0;
    my $name = '';
    GetOptionsFromArray(
        \@ARGV,
        "enable" => \$enable,
        "name=s" => \$name,
    ) or pod2usage(2);

    print "subcommand 'foo'\n";
    print "  enable: ", $enable ? "true" : "false", "\n";
    print "  name: $name\n";
    print "  tail: [", join(", ", @ARGV), "]\n";
}

sub cmd_bar {
    my $level = 0;
    GetOptionsFromArray(
        \@ARGV,
        "level=i" => \$level,
    ) or pod2usage(2);

    print "subcommand 'bar'\n";
    print "  level: $level\n";
    print "  tail: [", join(", ", @ARGV), "]\n";
}

main();

__END__

=head1 NAME

subcommands - Example of command-line subcommands

=head1 SYNOPSIS

subcommands foo [options] [args]
subcommands bar [options] [args]

 Options for foo:
   --enable          Enable flag
   --name=STRING     Name option

 Options for bar:
   --level=INT       Level option

=cut

To run the program, save it as subcommands.pl and make it executable:

$ chmod +x subcommands.pl

First, invoke the foo subcommand:

$ ./subcommands.pl foo --enable --name=joe a1 a2
subcommand 'foo'
  enable: true
  name: joe
  tail: [a1, a2]

Now try bar:

$ ./subcommands.pl bar --level 8 a1
subcommand 'bar'
  level: 8
  tail: [a1]

But bar won’t accept foo’s flags:

$ ./subcommands.pl bar --enable a1
Unknown option: enable
Usage:
    subcommands bar [options] [args]

     Options for bar:
       --level=INT       Level option

This Perl script demonstrates how to create a program with subcommands, each having its own set of options. It uses the Getopt::Long module to parse command-line options and Pod::Usage for generating usage information.

The structure is similar to the original example, with separate functions for each subcommand (cmd_foo and cmd_bar). The main function checks the first argument to determine which subcommand to run, then delegates to the appropriate function.

Each subcommand function uses GetOptionsFromArray to parse its specific options, allowing for different sets of flags for each subcommand. Any remaining arguments are treated as positional arguments.

This approach allows for creating complex command-line tools with multiple subcommands, each with its own set of options and behaviors.