pericmd 003: What’s wrong with Getopt::Long

So what’s wrong with Getopt::Long? Nothing really. As an option parser library it’s doing perfectly okay. There are some crufts or things I personally would like to change (I’ll talk about it later), but these are minor. Also there are alternative libraries with different/”fresh” approaches like Docopt which improve options parsing in some aspects, albeit introducing new problems of their own (we’ll also get into these later), but really the old Getopt::Long way is still fine for most cases.

As a CLI library, however, it is inadequate. Well, that’s unfair. What I meant to say is that Getopt::Long is not a full CLI library. It can be part of such library, but a CLI library will want functionalities not provided by Getopt::Long. For example, routing, which is just a fancy way of saying a nice way of mapping options to actions/commands. Getopt::Long does not provide such routing; it only deals mostly with assigning values to options. So, in a more complex CLI application you’ll see a cascade if statement like this:

GetOptions(
    'clean'      => sub { $action = "clean" },
    'register'   => sub { $action = "register" },
    'unregister' => sub { $action = "unregister" },
    'install'    => sub { $action = "install" },
    'uninstall'  => sub { $action = "uninstall" },
    ...
);

if ($action eq 'clean') {
    ...
} elsif ($action eq 'register') {
    ...
} elsif ($action eq 'unregister') {
    ...
} elsif ($action eq 'install') {
    ...
} elsif ($action eq 'uninstall') {
    ...
}

It would be nice if a CLI library lets us specify a routing of options/subcommands to actions, much like what we usually see in web frameworks.

Technically speaking, you can perform action directly in an option handler, like I usually do with --help or --version, but this will be done immediately when that option is encountered by Getopt::Long so you cannot see the options that follow it (this is usually okay for help/version message because they don’t need to read options and exit early anyway):

GetOptions(
    'help|h|?'  => sub { say "help message ..."; exit 0 },
    'version|v' => sub { say "foo version $VERSION"; exit 0 },
    ...
);

Another feature that Getopt::Long does not provide is automatic help message generation. Well, actually Getopt::Long does have an auto_help switch, but this will only dump the POD instead of generating a help message from its options specification. The main reason is that the specification does not contain rich enough (meta)data to generate a proper help message. Getopt::Long::Descriptive attempts to remedy this, and I’ll talk about it too in another post.

Also missing from Getopt::Long is the concept of subcommands, which most CLI application will need to have, when they grow big/complex enough, e.g. git, perlbrew. So Getopt::Long is purely all about options and does not care about arguments or subcommands at all. There are other libraries, some of them based on Getopt::Long, that tackle this, and we’ll talk about those libraries in the future.

3 thoughts on “pericmd 003: What’s wrong with Getopt::Long

    • I haven’t personally used them yet (not even GL::Descriptive), but given the wandering nature of my blog series, I’ll eventually get to these Moose/Moo CLI libraries 🙂

      Like

  1. Pingback: Getopt modules 01: Getopt::Long | perlancar's blog

Leave a comment