milanHrabos
milanHrabos

Reputation: 1965

How to provide more arguments (separated by space) for one option in Getopt::Std in perl?

I would like to specify more arguments for one option:

#!/usr/bin/perl -w
use Getopt::Std qw[getopts];

getopts('a:');

if($opt_a){
    print "$opt_a\n";
}

and the use it like

$ foo.pl -a foo bar #...

and get each specified file/argument to an array in @opt_a, is it possible?

Upvotes: 1

Views: 289

Answers (2)

ikegami
ikegami

Reputation: 385657

Forget Getopt::Std for a second. How would a module (or any other code) know that bar was an value for a and not the first positional argument? Without a means of doing that, your grammar is ambiguous, and a parser can't be written for it.

This can be resolved in one of three ways. (Well, there are surely others.)

Solution 1 Allow the option and the flag to be repeated.

prog -a foo -a bar pos0 pos1
use Getopt::Long qw( GetOptions );

GetOptions(
   '?|h|help' => \&help,
   'a=s'      => \my @opt_a,
)
   or usage();

Solution 2 Use -a to indicate what the positional arguments mean.

prog -a foo bar
use Getopt::Long qw( GetOptions );

GetOptions(
   '?|h|help' => \&help,
   'a'        => \my @opt_a,
   'b'        => \my $opt_b,
)
   or usage();

( $opt_a ? 1 : 0 ) + ( $opt_b ? 1 : 0 ) == 1
   or usage("-a or -b must be specified, but not both");

my $opt_action = $opt_a ? 'a' : 'b';

@ARGV > 0
   or usage("Invalid number of arguments");

Solution 3 Assume all values following -a belong to -a

It turns out there is a standard way of marking the start of positional arguments. Using -- would distinguish option values from positional arguments.

prog -a foo bar -- pos0 pos1
use Getopt::Long qw( GetOptions );

GetOptions(
   '?|h|help' => \&help,
   'a=s{1,}'  => \my @opt_a,
)
   or usage();

However, -- is usually used to protect against positional arguments start with -. It's a weird thing to have to protect against positional arguments not starting with -.

It's also error-prone. People will be tempted to do the following:

prog -a foo pos0    # XXX Doesn't work as expected!

Notes:

  1. I used Getopt::Long. If Getopt::Std can do it too, great. I used what I knew.
  2. See this for sample implementation of help and usage.

Upvotes: 2

sticky bit
sticky bit

Reputation: 37472

Apparently this is not possible with Getopt::Std. But Getopt::Long features an experimental feature that seems to provide what you want.

#!/usr/bin/perl

use strict;
use warnings;

use Getopt::Long;

my @args_a;

GetOptions('a=s{1,}' => \@args_a);

foreach my $arg_a (@args_a) {
  print("$arg_a\n");
}

When the above is called with -a x y z then @args_a = ('x', 'y', 'z').

But, as ikegami pointed out, you have to find a way to distinguish "flagless" parameters (if you intend to have such). You can use -- to separate them. A call with -a x y z -- n m o would result in @args_a = ('x', 'y', 'z') and @ARGV = ('n', 'm', 'o').

And of course it's a risk to write a program that relies on an experimental feature which might be removed in later versions.

An alternative, that doesn't rely on an experimental feature (but still needs Getopt::Long), would be to allow multiple -as.

...
GetOptions('a=s' => \@args_a);
...

would do that. Here to get @args_a = ('x', 'y', 'z'), the call needs to be with -a x -a y -a z.

Upvotes: 2

Related Questions