Search code examples
perlhashgetopt-long

Perl: store multi-valued cmd line argument directly into hash keys


I'm adding some command line parsing to a Perl script and I'm trying to figure out how to store the multiple values of a single command line argument as keys of a hash.

The following MWE hopefully gets at what I want to do.

# foo.pl    
use Data::Dumper;

use Data::Dumper::Simple;
use Getopt::Long;

my %opt=();

my @tempSuppress;

GetOptions ('verbose' => \$opt{verbose},
            'suppress:s{1,}' => \@tempSuppress,
            );

print "@tempSuppress\n";


my $tempvar = shift @tempSuppress;
$opt{suppress}{$tempvar}=1;
$tempvar = shift @tempSuppress;
$opt{suppress}{$tempvar}=1;
# Yes, the above 4 lines could be in a loop

warn Dumper(%opt);

if($opt{verbose}){
    print "Some verbose output\n";
}

if(!$opt{suppress}{E}){
    print "Some default output you can suppress with option suppress value 'E'\n";
}

if(!$opt{suppress}{F}){
    print "Some other default output you can suppress with option suppress value 'F'\n"
}

As you can see from my demo print statements, I'd like to be able to use the various keys of %opt as booleans to enable/disable various sections of the code. Pretty straightforward I think.

There are also a couple different types of output that generally will be on by default, but I want the option to suppress it. I have the argument suppress that can take multiple values to denote which types to suppress, e.g. you might run foo.pl --suppress E F.

I was hoping to have each of them become a hash key as well so it would still be quick & easy to refer to them, as I've done later in my code.

After reading the documentation on options with multiple values, I see how to store those two values into an array, and as you can see in my code, I can copy them out into sub-keys of the suppresss key in opt. I was hoping that that process of getting them into those sub-keys could be integrated into the call to GetOptions, e.g. something like the following:

GetOptions ('verbose' => \$opt{verbose},
            'suppress:s{1,}' => \$opt{suppress},
            );

But after trying that, I see it just gives the suppress key of opt the value of the last value processed (e.g. 'F' if you passed in "E F"), rather than making keys for each value and giving them a value of 1.

Thanks.


Solution

  • There's a couple ways to accomplish this.

    First uses Getopt::Long's built in hash options but to make it require requires changing how you handle your options. To say a value is a hash you add a %.

    'suppress:s%' => \$opt{suppress}
    

    But Getopt::Long expects multiple values to be passed in with multiple uses of --suppress. This is how many programs do multiple argument handling.

    program --suppress E --suppress F
    

    If you try --suppress E F it will read that as --suppress E and leave F on @ARGV.

    Furthermore, % usually works with --option key=value. In your case $opt{suppress}{E} will have no value. Instead of if( $opt{suppress}{E} ) you'll have to check if( exists $opt{suppress}{E} ).


    The second option is to use a user defined function. This has the advantage of being reusable for multiple options.

    sub listopt_to_hash {
        my($name, $val) = @_;
        $opt{$name}{$val} = 1;
        return;
    }
    
    GetOptions(
        'verbose' => \$opt{verbose},
        'suppress:s{1,}' => \&listopt_to_hash
    );
    

    The final option is to use a different module. There's many, many, many Getopt modules on CPAN and one probably does what you want.