framontb
framontb

Reputation: 2077

Parsing command line mutual exclusion flags with their specific options in perl with Getopt::Long

I have several mutually exclusive flags witch have their own options. Lets say, if I invoke the "stop_service" flag, I want a "name" option; but if I invoke the "send_report" flag I want a "email" option. For parsing that I use "Getopt::Long". This is the code:

use Getopt::Long;

# Option vars
my $stop_service;   # flag
my $send_report;    # flag
my $name;           # string
my $email;          # string

# Get all possible options
GetOptions(
    # Flag and options for stop_service
    "stop_service"  => \$stop_service,      # Mutual Exclusion Flag
    "name=s"        => \$name,              # option string

    # Flag and options for send_report
    "send_report"   => \$send_report,       # Mutual Exclusion Flag
    "email=s"       => \$email,             # option string
);

# Parsing correct combinations
# --stop_service --name XXX
if (($stop_service and !$send_report)       # mutual exclusion
    and ($name && !$email))                 # options
{
    print "stop_service + name: \n";
    print $stop_service, " - ", $name, "\n";
}
# --send_report --email XXX
elsif ((!$stop_service and $send_report)    # mutual exclusion
    and (!$name && $email))                 # options
{
    print "send_report + email: \n";
    print $send_report, " - ", $email, "\n";
}
# HELP
else {
print <<DOC;
    Help in line 1.
    Help in line 2.
DOC
}

It works well:

[getopt]$ perl 06_getopt_cond_3.pl --stop_service --name jumersindo
stop_service + name: 
1 - jumersindo
[getopt]$ perl 06_getopt_cond_3.pl --send_report --email [email protected]
send_report + email: 
1 - [email protected]
[getopt]$ perl 06_getopt_cond_3.pl --send_report --name
Option name requires an argument
    Help in line 1.
    Help in line 2.

Are there a more "automatic" way of configuring that? or I need to specify all the option combinations with the "if" sentences?

Upvotes: 1

Views: 161

Answers (1)

framontb
framontb

Reputation: 2077

Based on Avoiding mix of certain arguments to script, I managed to implement this solution. The idea is only accept the explicitly specified options and reject other combinations, like that:

use strict;
use Getopt::Long;

# Option vars
my %options= ();    

# Get all possible options
GetOptions(
    # Mutually exclusive flags
    "detener_servicio|stop_service"         => \$options{stop_service},
    "arrancar_servicio|start_service"       => \$options{start_service},
    "reiniciar_servicio|restart_service"    => \$options{restart_service},
    "registrar_servicio|record_service"     => \$options{record_service},
    "enviar_informe|send_report"            => \$options{send_report},

    # Options
    "nombre|name=s"                         => \$options{name},
    "script=s"                              => \$options{script}, 
    "ruta|path=s"                           => \$options{path}, 
    "ejecutables|execs=s"                   => \$options{execs},
    "email=s"                               => \$options{email},
);

if (only_specified_options(\%options, 'stop_service', 'name')) {
    print "Stoping service: ", $options{name}, "\n";
}
elsif (only_specified_options(\%options, 'start_service', 'name')) {
    print "Starting service: ", $options{name}, "\n";
}
elsif (only_specified_options(\%options, 'restart_service', 'name')) {
    print "Restarting service: ", $options{name}, "\n";
}
elsif (only_specified_options(\%options, 'record_service', 'name', 
                                         'script','path','execs')) {
    print "Recording service: ", $options{name},    " ", 
                                 $options{script},  " ", 
                                 $options{path},    " ", 
                                 $options{execs} ,  " ", "\n";
}
elsif (only_specified_options(\%options, 'send_report', 'email')) {
    print "Sending report: ", $options{email}, "\n";
}
else {
    print <<DOC;
Usage:
    script --stop_service    --name <NAME>
    script --start_service   --name <NAME>
    script --restart_service --name <NAME>
    script --record_service  --name <NAME> --script <SCRIPT> 
                             --path <PATH> --execs <EXECS>
    script --send_report     --email <EMAIL>
DOC
}

# only_specified_options(\%options, 'option_1', 'option_2',..., 'option_n')
# If only specified options are present => return true
#   otherwise => false
sub only_specified_options {
    my $opt_ref = shift;
    my %must_params = map { $_ => 1 } @_;
    my $result_bool = 1;
    
    while ((my $key, my $value) = each (%$opt_ref)) {
        $result_bool &&= (exists($must_params{$key})?$value:!$value);
    }
    return $result_bool;
}

Upvotes: 2

Related Questions