Beefy Boxes and Bandwidth Generously Provided by pair Networks
The stupid question is the question not asked
 
PerlMonks  

Parsing command line options without knowing what they are

by DrWhy (Chaplain)
on Nov 25, 2010 at 00:58 UTC ( #873562=perlquestion: print w/ replies, xml ) Need Help??
DrWhy has asked for the wisdom of the Perl Monks concerning the following question:

Greetings monks,

This seems like such a simple idea, but I can't seem to find any module that does it. I have a script that needs to take in a bunch of command line options in an open-ended set. I don't want to name them all in the script. So I want a GetOptions() function that has the ability to just process the command line arguments and treat the things that look like options as if they were options and not throw up all over my screen just because I didn't tell it ahead of time that this these options might be coming in. (And also not just ignore them and leave them in @ARGV.)

perl -s kind of does this, but I need it to be able to handle long options names that take values and don't require '=' between option name and value. I guess I'd like to see an addition like this for Getopt::Long::GetOptions:

use Getopt::Long; GetOptions( opta => \$opta, 'optb=s' => \$optb, '=s' => \%others, #here's the new thing. );
where '=s' says for anything else that looks like a command line options process it as if it were and as if it required a string argument, stuffing the results as key/value pairs in %others.

Or am I just being dense and there's some really straightforward way to do this that I'm not seeing?

--DrWhy

"If God had meant for us to think for ourselves he would have given us brains. Oh, wait..."

Comment on Parsing command line options without knowing what they are
Download Code
Re: Parsing command line options without knowing what they are
by PeterPeiGuo (Hermit) on Nov 25, 2010 at 01:26 UTC

    Check out the "Mixing command line option with other arguments" section of Long::GetOptions.

    Peter (Guo) Pei

Re: Parsing command line options without knowing what they are
by aquarium (Curate) on Nov 25, 2010 at 01:32 UTC
    and also check the storing options in a hash documentation, as you don't need to pre-specify all options beforehand.
    the hardest line to type correctly is: stty erase ^H
      This is the first thing I tried, but it doesn't do what I want. Yes, you can give it a hash reference and it will dump options in there, but you still have to name all the options that you want to support in the GetOptions() call. I have currently about 300 options that are supported, and this set will change over time. I don't want to have to keep going back to this code and changing the list here every time we add a new feature or remove an old one. What I need is something where I can give it a hash and it fills it up with whatever it finds on the command line without having to name each possible option.

      --DrWhy

      "If God had meant for us to think for ourselves he would have given us brains. Oh, wait..."

        maybe Getopt::Regex?, and use a catch all regex with a sub to process each argument, e.g. store in hash or array.
        the hardest line to type correctly is: stty erase ^H
Re: Parsing command line options without knowing what they are
by GrandFather (Cardinal) on Nov 25, 2010 at 06:32 UTC

    I don't know how your code is structured so may be the following doesn't apply, but if I were faced with the issue of code changes requiring their own sets of command line options I'd modularise the code and have each module register the options it uses at start up. That way the registration code can perform conflict checking and build the allowed list of commands for Getopt::Long at the same time.

    True laziness is hard work
Re: Parsing command line options without knowing what they are
by BrowserUk (Pope) on Nov 25, 2010 at 08:38 UTC

    I never use getOpts of any form, and rarely use programs that use those conventions, so I've probably missed some rules. And it would probably need to be invoked at BEGIN{} time to be effective, but it's easier to play with this way.

    This is hardly exhaustively tested, but it might form the basis of something useful:

    #! perl -lw use strict; use Data::Dump qw[ pp ]; sub getOpts { my %opts; my @toDelete; for( my $i=0; $i < @ARGV; ++$i ) { $_ = $ARGV[ $i ]; if( m[^-(.)(.*)$] ) { my( $first, $rest ) = ( $1, $2 ); push @toDelete, $i; if( ! defined $first ) { next; } elsif( $first eq '-' ) { if( $rest eq '') { last; } if( $ARGV[ $i+1 ] =~ m[^-] ) { if( $rest =~ m[^no(.+)$] ) { $opts{ $1 } = 0; } else { $opts{ $rest } = 1; } } else { $opts{ $rest } = $ARGV[ ++$i ]; push @toDelete, $i; } } else { $opts{ $_ } = 1 for $first, split'',$rest; } } } splice @ARGV, $_, 1 for reverse @toDelete; return %opts; } my %opts = getOpts(); print "@ARGV\n", pp \%opts; __END__ c:\test>perl getOpts.pl -abc --def --ghi jkl -m -o fred bill --tuv wxy +z fred bill { a => 1, b => 1, c => 1, def => 1, ghi => "jkl", "m" => 1, o => 1, tu +v => "wxyz" } c:\test>perl getOpts.pl -abc --def --ghi jkl -m -- -o fred bill --tuv +wxyz -o fred bill --tuv wxyz { a => 1, b => 1, c => 1, def => 1, ghi => "jkl", "m" => 1 } c:\test>perl getOpts.pl -abc 12345 --def --ghi jkl -m -- -o fred bill +--tuv wxyz 12345 -o fred bill --tuv wxyz { a => 1, b => 1, c => 1, def => 1, ghi => "jkl", "m" => 1 } c:\test>perl getOpts.pl -abc 12345 --def --ghi jkl -m -o fred bill --t +uv wxyz 12345 fred bill { a => 1, b => 1, c => 1, def => 1, ghi => "jkl", "m" => 1, o => 1, tu +v => "wxyz" } c:\test>perl getOpts.pl -abc --def 12345 --ghi jkl -m -o fred bill --t +uv wxyz fred bill { a => 1, b => 1, c => 1, def => 12345, ghi => "jkl", "m" => 1, o => 1 +, tuv => "wxyz" } c:\test>perl getOpts.pl -abc --def 12345 --noverbose --ghi jkl -m -o f +red bill --tuv wxyz fred bill { a => 1, b => 1, c => 1, def => 12345, ghi => "jkl", "m" => 1, o => 1 +, tuv => "wxyz", verbose => 0 } c:\test>perl getOpts.pl -abc --def 12345 --noverbose --ghi jkl -m -o f +red --o bill --tuv wxyz fred { a => 1, b => 1, c => 1, def => 12345, ghi => "jkl", "m" => 1, o => " +bill", tuv => "wxyz", verbose => 0 }

    Examine what is said, not who speaks -- Silence betokens consent -- Love the truth but pardon error.
    "Science is about questioning the status quo. Questioning authority".
    In the absence of evidence, opinion is indistinguishable from prejudice.
      Thanks, I may use some of the ideas in here for my own parser.

      --DrWhy

      "If God had meant for us to think for ourselves he would have given us brains. Oh, wait..."

        YW. I later looked inside some of the existing getopts modules to see what other rules they implement, and boy are they ever complicated. Reverse engineering the rules from them would be a nightmare.

        Here's a slightly cleaner refactoring of the above:

        sub getOpts { my %opts; my @toDelete; for( my $i=0; $i < @ARGV; ++$i ) { $_ = $ARGV[ $i ]; if( m[^-(.)(.*)$] ) { my( $first, $rest ) = ( $1, $2 ); push @toDelete, $i; next unless defined $first; if( $first ne '-' ) { $opts{ $_ } = 1 for $first, split'',$rest; next; } last if $rest eq ''; if( $ARGV[ $i+1 ] !~ m[^-] ) { $opts{ $rest } = $ARGV[ ++$i ]; push @toDelete, $i; } if( $rest =~ m[^no(.+)$] ) { $opts{ $1 } = 0; } else { $opts{ $rest } = 1; } } } splice @ARGV, $_, 1 for reverse @toDelete; return %opts; }

        Examine what is said, not who speaks -- Silence betokens consent -- Love the truth but pardon error.
        "Science is about questioning the status quo. Questioning authority".
        In the absence of evidence, opinion is indistinguishable from prejudice.

Log In?
Username:
Password:

What's my password?
Create A New User
Node Status?
node history
Node Type: perlquestion [id://873562]
Approved by ww
help
Chatterbox?
and the web crawler heard nothing...

How do I use this? | Other CB clients
Other Users?
Others contemplating the Monastery: (20)
As of 2014-07-11 17:42 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    When choosing user names for websites, I prefer to use:








    Results (232 votes), past polls