Exclusive command line options in Perl applications
I almost always use Getopt::Long::Descriptive to parse the command line arguments in my scripts and applications. One common use case I have is to use an option to instrument the mode the application is going to work, but ensuring that no more then one mode has been specified. For instance, imagine the application could have the following options (among the others):--nuketo destroy the database content;--initto initialize the database;--backupto backup the database content.
A manual approach
A manual approach, is to count how many options have been specified on the command line and interrupt the application if more than one is found:use Getopt::Long::Descriptive;
my ($opts, $usage) = describe_options(
'myapp.pl %o <some-arg>',
[ nuke => 'delete database content' ],
[ init => 'initialize the database' ],
[ backup => 'backup the database' ],
);
my $exclusive_options = 0;
for ( qw/ nuke init backup / ) {
$exclusive_options++ if ( $opts->$_ );
}
say $usage and exit if ( $exclusive_options > 1 );
# ... application continues ...
The main idea is to handle into the
$exclusive_options the number of options specified on the command line, exiting if the counting is greater than 1.
The main disadvantage of this approach is that whenever the application gains a new option, both the Getopt::Long::Descriptive and the code block must be updated, so this approach is error prone.
Getopt::Long::Descriptive approach
Luckily, Getopt::Long::Descriptive provides a way to bind an option to a set of enumerated values.
Moreover, since an option can be marked as hidden, it will not appear on the help screen, making this approach seamless.
use Getopt::Long::Descriptive;
my ($opts, $usage) = describe_options(
'myapp.pl %o <some-arg>',
[ do => hidden =>
{ one_of => [
[ nuke => 'delete database content' ],
[ init => 'initialize the database' ],
[ backup => 'backup the database' ],
] } ],
);
# ... application continues here ...
The idea is to define a
do option, that being hidden will not be shown on the $usage help screen.
The trick is to use one_of that accepts an array ref with all the options.
The final result will be that only the three one_of options will be specified on the $usage help screen, and setting more than option at the same time will result in the application stopping automatically:
% perl ~/tmp/test.pl --nuke --init
The 'nuke' parameter ("1") to "eval" did not pass the 'nuke implies do=nuke' callback: these options conflict; each wants to set the do: init nuke
myapp.pl [long options...] <some-arg>
--nuke delete database content
--init initialize the database
--backup backup the database
The error message reveals the trick: the
one_of on do means that anyone of the option will set also the hidden do option, but only one will be authorized at a time.
In other words, passing for instance --nuke will result in $opts->do and $otps->nuke being set at the same time.
With this trick in mind, it is possible to enhance the application skeleton as follows:
use Getopt::Long::Descriptive;
my ($opts, $usage) = describe_options(
'myapp.pl %o <some-arg>',
[ do => hidden =>
{ one_of => [
[ nuke => 'delete database content' ],
[ init => 'initialize the database' ],
[ backup => 'backup the database' ],
] } ],
);
say $usage and exit if ( ! $opts->do ); # do not know what to do!
# ... application continues here ...
I suggest you to pick a better name than
do, since it is a Perl operator, but that works for me.