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

I have a utility where I would like to be able to use a combination of Getopt::Long and single keywords on the command line e.g.
usage args.pl [-s] [-b] [-u] update [server <server>] [user <user>] [passwor +d <password>]
Where the -s , -b and -u are handled by GetOptions
GetOptions('help|h!' => \$help, 'b!' => \$backup, 'u!' => \$upd_sybase, 's!' => \$silent);
Can I handle the other arguments like update,server,user,password via GetOptions without having to use a hyphen in front ?
e.g. I would like to avoid this
args.pl [-s] [-b] [-u] -update [-server <server>] [-user <user>] [-pas +sword <password>]
I understand there may be issues with mixing GetOptions functions and the contents of ARGV being destroyed when you call them
Any help appreciated

Replies are listed 'Best First'.
Re: Mixing command line arguments
by bpphillips (Friar) on Nov 02, 2004 at 15:39 UTC
    Getopt::Long leaves any unknown data that it finds in @ARGV alone. So you could process whatever's left in @ARGV yourself after sending it through GetOptions():
    my($help,$backup,$upd_sybase,$silent); GetOptions('help|h!' => \$help, 'b!' => \$backup, 'u!' => \$upd_sybase, 's!' => \$silent); # assuming it's always key1 value1 key2 value2 in argument list my %other_args = @ARGV; # if your arguments are positional, do this: # my($server,$password) = @ARGV; # called like this: ./foo --silent server bar password blah # yields: # $silent == 1 # $other_args{server} eq "bar"; # $other_args{password} eq "blah";
    Update: Added an example for positional arguments since the OP seemed to maybe have wanted that --Brian

      Another possibility would be to preprocess @ARGV and prefix "--" to any keywords you want to recognize (e.g. turn update into --update) then pass it to Getopt::Long and let it do all the work.

      Update: Changed my example keyword to match original sample invocation.

      Copying @ARGV into a hash doesn't work because "update" has no value. You end up with:

      %other_args = ( update => 'server', weitorsv015 => 'user', ebrine => 'password', secret => undef, );

      instead of

      %other_args = ( update => undef, server => 'weitorsv015', user => 'ebrine', password => 'secret', );
Re: Mixing command line arguments
by ikegami (Patriarch) on Nov 02, 2004 at 17:45 UTC
    Simply redefine the prefix:
    use Getopt::Long; Getopt::Long::Configure('prefix_pattern=(?:--|-)?'); $rv = GetOptions( 'help|h!' => \$help, 'b!' => \$backup, 'u!' => \$upd_sybase, 's!' => \$silent, 'update' => \$update, 'server=s' => \$server, 'user=s' => \$user, 'password=s' => \$password, ); push(@args, $help ? "--help" : "--nohelp") if defined $help; push(@args, $backup ? "--b" : "--nob" ) if defined $backup; push(@args, $upd_sybase ? "--u" : "--nou" ) if defined $upd_sybase; push(@args, $silent ? "--s" : "--nos" ) if defined $silent; push(@args, "update" ) if defined $update; push(@args, "server=$server" ) if defined $server; push(@args, "user=$user" ) if defined $user; push(@args, "password=$password" ) if defined $password; print(join(' ', @args), $/);

      I realize this wasn't an integral part of your example, but your series of push lines would be very disheartening for me to see in real code. For something like that, I'd much rather see a data-driven solution. Something along the lines of:

      my @args; my %chkopts = ( help => $help, b => $backup, u => $upd_sybase, s => $silent, ); while (my ($k, $v) = each %chkopts) { next unless defined $v; push @args, $v ? "--$k" : "--no$k"; } my %chkargs = ( update => $update, server => $server, user => $user, password => $password, ); while (my ($k, $v) = each %chkargs) { next unless defined $v; push @args "$k=$v"; }

      This way, the data (i.e., which args and opts need to be checked, and what their values are) is separated from the policy (i.e., build an array holding the defined args/opts and their values, using a certain syntax for args and a different syntax for opts). Adding new args or opts would be a much simpler process, and checking the data for errors would be easier. At some point, this data could even be moved out of the code altogether, and put in a config file.