1

Lets take a simple example: I wanto to have the following input in my script:

[
{foo => date_1},
{foo2=> date_2},
{foo3=> undef}, # there was no input
... # probably more in the future
]

So using something similar to this:

use strict;
use warnings;
use utf8;
use Getopt::Long qw(GetOptions);

my @foos;
my @dates;

# INPUT
GetOptions(
    'foo|f:s'                                       => \@foos,
    'date|d:s'                                      => \@dates,
# 'help|h'   => \&help,
) or die "Invalid options passed to $0\n"; 

I want to be able to call the script with somehing similar to this:

perl script.pl --foo "Go with 1" --foo "Go with 2" --date "date1" --date "date2"

And then be able to do somethig like:

foreach my $i (0..scalar(@foos)){
  print $foos[$i] . " " . $dates[$i] . "\n";
}

And obtain:

Go with 1 date1
Go with 2 date2

Why I want to do this?: I loop over an uncertain amount of foos for one function. In another function I want to loop again through the foos and print a date associated to it if present.

What I would like to avoid: Having to create a flag for every element of foos, like the following:

GetOptions(
    'foo|f:s'                                               => \@foos,
    'date-foo-1|df1:s'                                      => \$dates_foo_1,
    'date-foo-2|df2:s'                                      => \$dates_foo_2,
# 'help|h'   => \&help,
) or die "Invalid options passed to $0\n"; 

I think what I would to be able to do is having an optional flag associated to another flag but I have not been able to find anything related.

nck
  • 1,673
  • 16
  • 40

1 Answers1

1

Your code already does what you want.

use strict;
use warnings;
use utf8; # This is unnecessart
use Getopt::Long qw(GetOptions);

my @foos;
my @dates;

# INPUT
GetOptions(
    'foo|f:s'  => \@foos,
    'date|d:s' => \@dates,
# 'help|h'   => \&help,
) or die "Invalid options passed to $0\n";

foreach my $i (0 .. scalar(@foos)){
  print $foos[$i] . " " . $dates[$i] . "\n";
}

The output is:

Go with 1 date1
Go with 2 date2
Use of uninitialized value in concatenation (.) or string at opts.pl line 17.
Use of uninitialized value in concatenation (.) or string at opts.pl line 17.

There's a warning displayed because your array-walking code has a bug. You don't want to walk 0 .. scalar @foos as scalar @foos gives 2 and the highest index in @foos is 1.

Instead of scalar @foos, you can use $#foos to get the highest index in @foos.

foreach my $i (0 .. $#foos){
  print $foos[$i] . " " . $dates[$i] . "\n";
}

It's also worth pointing out that

print $foos[$i] . " " . $dates[$i] . "\n";

Can be written more simply as:

print "$foos[$i] $dates[$i]\n";

Update: In a situation where you need two lists of the same length, you might also look at the documentation for Options with hash values

Dave Cross
  • 68,119
  • 3
  • 51
  • 97
  • The problem is that that will only work as long as all dates have a value, but if I want to have a date in the third and in the first option it will not work, as the third option will be shifted to the second option giving undesired results. – nck Aug 11 '20 at 12:38
  • @nck: Then I think you need to look at the hash option, don't you? – Dave Cross Aug 11 '20 at 12:47
  • correct me if I´m wrong, but I think the hash values option allows you to store a set of options into a hash, but there it is only taking a subset of the input parameters into a hash, it does not solve the problem of having unmatched parameters – nck Aug 12 '20 at 11:34
  • I'm thinking you'd have to change the way the parameters work: `--item foo=date1 --item foo2=date2 --item foo3=` etc... – Dave Cross Aug 12 '20 at 12:54
  • unfertonetaly that approach is not really scalable and not intuitve to the user:( – nck Aug 14 '20 at 12:30