TTaJTa4
TTaJTa4

Reputation: 840

Cleanest way to parse argument with Getopt::Long

I use GetOpt to parse command-line arguments. I would like to add a new option "multi" which should get a string which looks as following: key1=abc,key2=123,key3=xwz. I don't know how many custom keys user want to give but he can give minimax 5 keys. Also, I would like to put it in a hash with keys. I'm looking for a good and clean way to implement it.

For starters, I thought of using --multi {key1=abc,key2=123,key3=xwz} but for some reason, it gets only the first key key1=abc. Also I tried: --multi {key1=abc},{key2=123},{key3=xwz} but it feels kind of messy. I want to give the user the possibility to add arguments with - like key1=./some_script.pl --help. Part of the code:

my %arg;
GetOptions(
            "multi=s" => \$arg{"multi"},
}

Then I would like to somehow put those keys in the hash so it will be easy to use them. So I thought of using: $arg{"multi"}{"key3"} in order to get the value of key3. How should I approach this feature? What is the cleanest way to do so?

To summarize it:

  1. What is the best way to ask the user to give keys in order to get a similar situation to key1=abc,key2=123,key3=xwz, without using a file (giving options, not in a file way)? Meaning - how would you like, as a user of the script, to give those fields?
  2. How to validate that user gave less than 5 keys?
  3. How should I parse those keys and what is the best way to insert those keys into the hash map in the multi key.

Expected output: I would like to have a hash which looks like this: $arg{"multi"}{"key3"} and returns xwz.

Upvotes: 2

Views: 137

Answers (2)

zdim
zdim

Reputation: 66883

One way is to assign options of key=value format to a hash, what GetOpt::Long allows. Even better, as this functionality merely needs a hash reference, it turns out that you can have it assign to a hashref that is a value inside a deeper data structure. You can make direct use of that

use warnings;
use strict;
use feature 'say';

use Getopt::Long;
use Data::Dump qw(dd);

my %args;
$args{multi} = {}; 

GetOptions( 'multi=s' => $args{multi} ) or die "Bad options: $!";

dd \%args;

With multiple invocations of that option the key-value pairs are added

script.pl --multi k1=v1 --multi k2=v2

and the above program prints

{ multi => { k1 => "v1", k2 => "v2" } }

I use Data::Dump to print complex data. Change to core Data::Dumper if that's a problem.

While Getopt::Long has a way to limit the number of arguments that an option takes that apparently applies only for array destinations. So you'd have to count keys to check.

Another way is to process the input string in a subroutine, where you can do practically anything you want. Adding that to the above script, to add yet another key with its hashref to %args

use warnings;
use strict;
use feature 'say';

use Getopt::Long;
use Data::Dump qw(dd);

my %args;
$args{multi} = {};

GetOptions(
    'multi=s' => $args{multi},
    'other=s' => sub { $args{other} = { split /[=,]/, $_[1] }  }
) or die "Bad options: $!";

dd \%args;

When called as

script.pl --multi k1=v1 --multi k2=v2  --other mk1=mv1,mk2=mv2

This prints

{
  other => { mk1 => "mv1", mk2 => "mv2" },
  multi => { k1 => "v1", k2 => "v2" },
}

Upvotes: 2

Corion
Corion

Reputation: 3925

The following program reads the comma-separated sub-options from the --multi option on the command line.

#!perl
use strict;
use warnings;
use Data::Dumper;
use Getopt::Long 'GetOptionsFromArray';

my @args = ('--multi', '{key1=abc,key2=123,key3=xwz}', 'some', 'other');

my %arg;
GetOptionsFromArray(
    \@args,
            "multi=s" => \$arg{"multi"},
);

if( $arg{multi} and $arg{multi} =~ /^\{(.*)\}$/) {
    # split up into hash:
    $arg{ multi } = { split /[{},=]/, $1 };
};

print Dumper \%arg;

__END__

$VAR1 = {
          'multi' => {
                       'key2' => '123',
                       'key1' => 'abc',
                       'key3' => 'xwz'
                     }
        };

The program uses GetOptionsFromArray for easy testability. In the real program, you will likely use GetOptions(...), which is identical to GetOptionsFromArray(\@ARGV, ...).

Upvotes: 3

Related Questions