Parsing unknown options

I’m not a ruby expert, so forgive me if the answer to my question is
obvious. I want to write a program which will load a plug-in
depending on an option passed in the command line. Each of the possible
plug-ins have their own options, which will also need to be parsed. For
example, if the main application accepts the -p option to select the
plug-in and the -a and -b options, I would like my program to be called
this way:

program -p myplugin -a -b -c -d

of course -c and -d are the options recognized by the plug-in. I’m not
sure on how to implement this. My initial idea was to use a two steps
approach:
1: parse the command line arguments looking only for the -p options and
ignoring the others. Once found the name of the plug-in, load it.
2: parse again the arguments, this time considering all the options,
including those accepted by the plug-in (which the plug-in itself will
tell the main program in some way).

The problem is that, as far as I can tell, OptParse will raise an
exception when it finds an unknown option (i.e as soon as it hits -c in
my example above). Is there a way around this behavior? Or does anybody
know of a better way to approach the whole problem?

Thanks in advance

Stefano

Stefano C. wrote:

I’m not a ruby expert, so forgive me if the answer to my question is
obvious.

This time, the answer really is obvious.

The problem is that, as far as I can tell, OptParse will raise an
exception when it finds an unknown option (i.e as soon as it hits -c in
my example above). Is there a way around this behavior? Or does anybody
know of a better way to approach the whole problem?

Sure. Parse the command line yourself. Scan the input arguments, select
those that begin with a dash, submit any second character to a case
statement. Use the case statement to distribute your actions. You would
have to do the latter no matter how you parsed the command line, and the
former is a few lines of code.


#!/usr/bin/ruby -w

ARGV.each do |com|
if com.length > 1 && com[0,1] == ‘-’
case com[1,1]
when ‘a’ then
puts “Option a”
when ‘b’ then
puts “Option b”
when ‘c’ then
puts “Option c”
when ‘d’ then
puts “Option d”
end
end
end

Paul L. wrote:

Stefano C. wrote:

The problem is that, as far as I can tell, OptParse will raise an
exception when it finds an unknown option (i.e as soon as it hits -c in
my example above). Is there a way around this behavior? Or does anybody
know of a better way to approach the whole problem?

Sure. Parse the command line yourself.

I don’t recommend it unless your syntax is simple or you like writing
command line parsers.

It just so happens that yesterday I wrote a command line parser that may
help in this case. I wrote it because getopt/long wasn’t deleting option
arguments from ARGV, so it was hard to use with ARGF. My parser leaves
any unknown options it finds in ARGV. It has some other nice features[1]
and only a few limitations[2]. Basically, it does one thing: turn an
array of strings into a hash of recognized options and their arguments,
leaving unrecognized strings in the original array.

I didn’t plan on releasing this today, but there’s really no reason not
to. It’s called argos[3]. For now it lives at:

http://redshift.sourceforge.net/argos.rb

The heart of it is simple enough (about 40 lines) to copy and paste into
a small ruby program file, if you don’t want to require it as a library.

Here’s a simple example:

 require 'argos'

 optdef = {
   "v"   => true,
   "n"   => proc {|arg| Integer(arg)}
 }

 argv = %w{-v -n10 filename}
 opts = Argos.parse_options(argv, optdef)
 p opts    # ==> {"v"=>true, "n"=>10}
 p argv    # ==> ["filename"]

[1] Features:

Output is a hash of {option => value, …}.

Supports both long (“–foo”) and short (“-f”) options.

A long option with an argument is --foo=bar or --foo bar.

A short option with an argument is -fbar or -f bar.

The options -x and --x are synonymous.

Short options with no args can be combined as -xyz in place of
-x -y -z.

The string “–” terminates option parsing, leaving the rest
untouched.

[2] Limitations:

A particular option takes either 0 args or 1 arg. There are no
optional arguments, in the sense of both “-x” and “-x3” being
accepted.

Options lose their ordering in the output hash (but they are
parsed in order and you can keep track using state in the handler
closures).

There is no usage/help output.

[3] Argos was Odysseus’ faithful dog, who was good at recognizing :wink:

Thanks for the answers. I decided to take an approach similar to
building my own parser, but keeping my two steps approach:

  • first:

    ARGV.each_with_index do |s, i|
    plugin=args[i+1] if s==’-p’ or s==’–plugin’ or s=~/-\w*p/
    end

(this should take care of the three possible formats: short option, long
option and several short options together, such as -abp plugin

  • load the plugin
  • use OptionParser to rescan the options

I’m still working on how to communicate the options recognized by the
plugin to the main program.

Stefano

Joel VanderWerf wrote:

I don’t recommend it unless your syntax is simple or you like writing
command line parsers.

My point is that a software solution shouldn’t be any more complex than
circumstances warrant. The OP might otherwise believe he needs to employ
a
package whose function he doesn’t understand, to solve a problem he
doesn’t
have.

On Tue, 14 Nov 2006, Stefano C. wrote:

  • load the plugin
  • use OptionParser to rescan the options

I’m still working on how to communicate the options recognized by the
plugin to the main program.

Stefano

OptionParser already handles unknown options:

begin
option_parser.parse! argv
rescue OptionParser::InvalidOption => e
# preverve unknown options
e.recover argv
end

-a

unknown wrote:

On Tue, 14 Nov 2006, Stefano C. wrote:

  • load the plugin
  • use OptionParser to rescan the options

I’m still working on how to communicate the options recognized by the
plugin to the main program.

Stefano

OptionParser already handles unknown options:

begin
option_parser.parse! argv
rescue OptionParser::InvalidOption => e
# preverve unknown options
e.recover argv
end

-a

Thanks for pointing this out. It’s exactly what I was looking for, and I
completely missed it. I’ve modified your code to store in a array the
unrecognized options and their arguments, if any is provided:

unknown=[] #this is the array where the unknown options will be put

begin
option_parser.parse! argv
rescue OptionParser::InvalidOption => e
e.recover argv
#recover just put the unknown option back into argv, so I extract it
again and put it into unknown
unknown << argv.shift

#if argv still contains some elements, and the first one doesn’t start
with a -, i.e is the argument for the unknown option, I remove it as
well
unknown << argv.shift if argv.size>0 and temp.first[0…0]!=’-’

go on with parsing

retry
end

puts unknown

Robert K. wrote:

excellent piece of software - actually this short example is just
another confirmation for that. But I always found that documentation of
OptionParser was a bit weak. Did I miss anything? Clearly the docs on
ruby-doc.org are rudimentary. Is there anyone that knows OptionParser
intimately and is willing to write up some documentation or at least add
some more examples to doc of class OptionParser that demonstrate
additional funcationality? It might be sufficient to just collect
working pieces of OptionParser code from the community, massage it a bit
and place it into the default docs. I would greatly appreciate that -
and probably others, too. What do you think?

I think that it would be a great idea. Personnaly, I always found that
looking at the code, documentation and trying was enough, but with
appropriate examples, that would be a lot easier. If the current
maintainers of OptionParser are reading this, that would be nice to tell
us how we could contribute to this ?

Would it make sense to put that documentation on a personal page,
waiting to be included upstream ? I’d be willing to do that, if anyone
wants to contribute.

Cheers !

Vince

On 14.11.2006 18:19, [email protected] wrote:

(this should take care of the three possible formats: short option, long
OptionParser already handles unknown options:

begin
option_parser.parse! argv
rescue OptionParser::InvalidOption => e
# preverve unknown options
e.recover argv
end

Thanks! Learn something new every day. I believe OptionParser to be an
excellent piece of software - actually this short example is just
another confirmation for that. But I always found that documentation of
OptionParser was a bit weak. Did I miss anything? Clearly the docs on
ruby-doc.org are rudimentary. Is there anyone that knows OptionParser
intimately and is willing to write up some documentation or at least add
some more examples to doc of class OptionParser that demonstrate
additional funcationality? It might be sufficient to just collect
working pieces of OptionParser code from the community, massage it a bit
and place it into the default docs. I would greatly appreciate that -
and probably others, too. What do you think?

Kind regards

robert

On Wed, Nov 15, 2006 at 06:05:08PM +0900, Robert K. wrote:

and probably others, too. What do you think?
I’ve been using OptionParser a fair bit. You can count me in if you
want to coordinate something. I’ll go through my code and pull out some
examples too.

enjoy,

-jeremy