Beefy Boxes and Bandwidth Generously Provided by pair Networks
Think about Loose Coupling
 
PerlMonks  

GetOpts::long Multiple Parameters per option

by PyrexKidd (Monk)
on Feb 06, 2011 at 22:47 UTC ( [id://886569]=perlquestion: print w/replies, xml ) Need Help??

PyrexKidd has asked for the wisdom of the Perl Monks concerning the following question:

Hello all,
I am working on taking command line switches.
I would like to have an option take two parameters on the command line. i.e.:

./program.pl -a param1 param2 -b param3 param4

Initially I wrote the script to take one parameter per switch.

#!/usr/bin/perl use strict; use warnings; use Getopt::Long; my %options = (); GetOptions ( "a=s" => \$options{'a'}, "b=s" => \$options{'b'}, "z!" => \$options{'z'}, ); my %Subs = ( 'a' => \&do_a, 'b' => \&do_b, 'z' => \&do_z, ); while (my ($sub_name, $function) = each %Subs){ $function->($options{$sub_name}); } sub do_a { my $active = shift; return unless $active; my $param1 = $active; # my $param2 = shift @ARGV; # print "$param1, $param2, \n"; } sub do_b { my $active = shift; return unless $active; my $param1 = $active; # my $param2 = shift @ARGV; # print "$param1, $param2, \n"; } sub do_z { my $active = shift; return unless $active; #do stuff without parameters }

To take two parameters I added the line:

my $param2 = shift @ARGV; #commented out in the code above

this works great if the parameters are passed in order:

$perl program.pl -a param1_a param2_a -b param1_b param2_b param1_a, param2_a, param1_b, param2_b,

but if I switch the order that I call the options the in the reverse order the second parameters are passed to the wrong sub:

$perl program.pl -b param1_b param2_b -a param1_a param2_a param1_a, param2_b, param1_b, param2_a,

Can anyone suggest a method for taking two parameters from the command line?

Replies are listed 'Best First'.
Re: GetOpts::long Multiple Parameters per option
by broomduster (Priest) on Feb 06, 2011 at 23:42 UTC
    I think you're making this too complicated. Check the docs for Getopt::Long. The last part of the section Options with multiple values shows how to do what you want.
    use feature qw(say); use strict; use warnings; use Getopt::Long; my( @opt_a, @opt_b ); GetOptions( 'a=s{2}' => \@opt_a, 'b=s{2}' => \@opt_b, ); say "@opt_a"; say "@opt_b";
    On the command line:
    ./pm-886569 -a a1 a2 -b b1 b2 a1 a2 b1 b2
    This example specifies exactly two arguments/parameters for each option, but note that you can specify a range for the number of options, if needed.

      Ok, I see what you mean.
      how about this:

      #!/usr/bin/perl use v5.10.1; use strict; use warnings; use Getopt::Long; my (@a, @b); my %options = ( 'a' => \@a, 'b' => \@b, ); GetOptions ( 'a=s{2}' => \@a, 'b=s{2}' => \@b, ); say "$options{'a'}->[0], $options{'a'}->[1]"; say "$options{'b'}->[0], $options['b'}->[1]";

      the only problem I have with this is defining parameters for %options before building %options with GetOptions
      If for instance I needed to have multiple pairs perl flag, i.e.:

      $./program -a a1_1 a1_2 -a a2_1 a2_2 -a a3_1 a3_2
      Thanks for the assist.

        the only problem I have with this is defining parameters for %options before building %options with GetOptions If for instance I needed to have multiple pairs per flag ...
        It still works if you handle it properly:
        #!/usr/bin/perl use v5.10.1; use strict; use warnings; use Getopt::Long; my (@a, @b); my %options = ( 'a' => \@a, 'b' => \@b, ); GetOptions ( 'a=s{2}' => \@a, 'b=s{2}' => \@b, ); # say "$options{'a'}->[0], $options{'a'}->[1]"; # say "$options{'b'}->[0], $options{'b'}->[1]"; say "@{ $options{'a'} }"; say "@{ $options{'b'} }";

        Firstly, don't dereference the array elements one at a time. Among other things, you get

        Use of uninitialized value in concatenation (.) or string at ./pm-886582 line 20.
        when there is no -b flag and args. But more important, you've hidden the fact that you really collected all of the arguments from all of the -a flags.

        Now you will see that:

        ./pm-886582 -a a1_1 a1_2 -a a2_1 a2_2 -a a3_1 a3_2 a1_1 a1_2 a2_1 a2_2 a3_1 a3_2
        (note that the output has an empty line where non-existent @{ $options{'b'} } is not printed.)

        IOW, all of the -a pairs are gathered into your @{ $options{'a'} }. All you need now is some code to check that the array has an even number of elements and then to take it apart into (multiple) pairs.

        Thanks for the assist.
        My pleasure. One last word of caution. The docs mention that this is an experimental feature. I haven't had any problems with it so far, but YMMV.

      I always like learning new things; I wasn't aware of this Getopt::Long feature.

      However, I prefer a different way of setting defaults, which mixes well with this:

      use Getopt::Long; my $opt = { grot => [qw( a bb ccc )], }; GetOptions( $opt, 'grot=s@{2}', ) or die "can't parse options";

Re: GetOpts::long Multiple Parameters per option
by tospo (Hermit) on Feb 07, 2011 at 13:51 UTC
    Or:
    my @a; my @b; GetOptions ( "a=s" => \@a, "b=s" => \@b ); @a = split(/,/,join(',',@a)); @b = split(/,/,join(',',@b)); if ( scalar(@a) !=2 || scalar(@b) !=2 ){ # shout at user here }
    The command line in that case would look like either of these (both will work)
    perl the_script -a 1 -a 2 -b 10 -b 30
    or
    perl the_script -a 1,2 -b 10,30
    I think the requirement to either specify the option name along with a value or use comma-separation makes the command a little clearer to read. Personal preference really.
      Re:
      GetOptions ( "a=s" => \@a, "b=s" => \@b ); @a = split(/,/,join(',',@a)); @b = split(/,/,join(',',@b));

      You don't have to parse after the fact; you can have the reference in the GetOptions() call be a sub ref and mangle things there. E.G.

      my $opt = { # this has to be first because the sub-refs # below are closures using it. grot => [], }; my $splitter = sub { my ($name, $val) = @_; push @{$opt->{$name}}, split q{,}, $val; }; GetOptions( $opt, 'grot=s@' => $splitter, ) or die "can't parse options";

      It's not so much useful in this case, where there is only one use, but if you have several options which can have multiple comma-separated values, this piece of code can be handy. I've also used other sub-refs in GetOptions, e.g. if the option value specifies a file-name, but you want the option to end up containing the file contents, you might use something like:

      my $from_file = sub { my ($name, $val) = @_; open my $fh, "<$val" or die "can\'t open file \'$val\'"; local $/ = undef; $opt->{$name} = <$fh>; close($fh); };

Log In?
Username:
Password:

What's my password?
Create A New User
Domain Nodelet?
Node Status?
node history
Node Type: perlquestion [id://886569]
Front-paged by Arunbear
help
Chatterbox?
and the web crawler heard nothing...

How do I use this?Last hourOther CB clients
Other Users?
Others admiring the Monastery: (2)
As of 2024-04-20 04:40 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found