4

Perl's Getopt::Long allows a developer to add their own options to a script. It's also possible to allow multiple values for an option by the use of a repeat specifier, as seen in regular expressions. For example:

GetOptions('coordinates=f{2}' => \@coor, 'rgbcolor=i{3}' => \@color);

Furthermore, option values can be stored in a hash, like so:

my %h = ();
GetOptions(\%h, 'length=i');    # will store in $h{length}

What I'm trying to do is, combine these two methods to end up with a hash of my options, even when they have multiple values.

As an example, say I want to allow three options: birthday (three integers), parents (one or two strings), first name (exactly one string). Let's also say that I want to put these values into a hash. I tried the following:

use strict;
use warnings;

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

my %h = ();
GetOptions(\%h, 'bday=i{3}', 'parents=s{1,2}', 'name=s{1}');

print Dumper(\%h);

And tested it, but the output was as follows:

perl optstest.pl --bday 22 3 1986 --parents john mary --name ellen
$VAR1 = {
    'name' => 'ellen',
    'parents' => 'mary',
    'bday' => 1986
};

Only the last value of each option is actually used in the hash. What I would like, though, is:

$VAR1 = {
    'name' => 'ellen',
    'parents' => ['mary', 'john'],
    'bday' => [22, 3, 1986]
};

If 'ellen' would be in an array, or if everything was inside a hash, that'd be fine as well.

Is it not possible to combine these two functionalities of Getopt::Long, i.e. putting options in a hash and using repeat specifiers?

Borodin
  • 126,100
  • 9
  • 70
  • 144
Bram Vanroy
  • 27,032
  • 24
  • 137
  • 239

3 Answers3

7
use Getopt::Long;
# enable for debugging purposes
# Getopt::Long::Configure("debug");
use Data::Dumper;

my %h = ();
GetOptions(\%h, 'bday=i{3}', 'parents=s@{1,2}', 'name=s@{1}');

print Dumper(\%h);

Is that what you want?

$VAR1 = { 
          'bday' => 1986,
          'name' => [ 
                      'ellen'
                    ],
          'parents' => [ 
                         'john',
                         'mary'
                       ]
        };
palik
  • 2,425
  • 23
  • 31
3

If you want an array, you need to give it a reference to an array.

local @ARGV = qw( --x y z );
my %h = ( x => [] );
GetOptions(\%h, 'x=s{2}');
print(Dumper(\%h));

Or you need to specify that you want an array.

local @ARGV = qw( --x y z );
GetOptions(\my %h, 'x=s@{2}');
print(Dumper(\%h));

Output:

$VAR1 = {
          'x' => [
                   'y',
                   'z'
                 ]
        };
ikegami
  • 367,544
  • 15
  • 269
  • 518
  • Thanks for the answer! I don't like the first option really. First of all because it will generate empty arrays even if an option is not specified in the CLI. This is simply not practical: simple checking for the existence of the option with `exists $h{'option'}` will always return true, even if not set. I cannot imagine that the developers of GetOpt intended this usage. The second suggestion does work fine though, so I'll use that. Thanks. – Bram Vanroy Apr 18 '17 at 18:08
1

The Options with multiple values section of the documentation that you link to also says this

Warning: What follows is an experimental feature.

It says earlier on

GetOptions (\%h, 'colours=s@'); # will push to @{$h{colours}}

so I guess that it was the author's intent for it to work the same way with repeat specifiers, and that you have found a bug

I suggest that you report it to the Perl 5 Porters using the perlbug utility that is part of the Perl installation

Borodin
  • 126,100
  • 9
  • 70
  • 144
  • 1
    Considering the other answers here, do you still think this is a bug? The suggested answer of using `@{1,2}` works fine, but perhaps you think it should also work without a type declaration? – Bram Vanroy Apr 18 '17 at 18:10
  • @BramVanroy: Absolutely. If the syntax that you used is incorrect then it shouldn't be accepted by the module. If it is correct then it should work. Under no circumstances should it silently "lose" all but one value of a parameter that has been specifically defined to be multi-valued. I'll raise the issue myself if you prefer? – Borodin Apr 18 '17 at 20:51
  • @BramVanroy: I guess I'm saying that `parents=s{1,2}` should always behave as `parents=s@{1,2}` because treating such a value as a simple scalar is nonsense. – Borodin Apr 18 '17 at 20:53