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.
Quite true, you might (or already) like MooX::Cmd linked with MooX::Options, that are using Getopt::Long::Descriptive underneath.
LikeLike
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 🙂
LikeLike
Pingback: Getopt modules 01: Getopt::Long | perlancar's blog