1

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?

sticky bit
  • 36,626
  • 12
  • 31
  • 42
milanHrabos
  • 2,010
  • 3
  • 11
  • 45
  • @ikegami I just to make a option to be able to get more arguments, for that option, not as position arguments (there are for different purpose), it is the same as some utilities have option to specify files, not one file to work with. So want the same with getops – milanHrabos Aug 01 '20 at 19:47

2 Answers2

2

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.
ikegami
  • 367,544
  • 15
  • 269
  • 518
2

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.

sticky bit
  • 36,626
  • 12
  • 31
  • 42