From: Robert Klemme Date: 2004-11-08T18:58:41+09:00 Subject: Re: From getoptlong to optparse ------=_NextPart_000_007A_01C4C581.B79561D0 Content-Type: text/plain; charset="iso-8859-1" Content-Transfer-Encoding: 7bit "Massimiliano Mirra - bard" schrieb im Newsbeitrag news:87k6sxr9zo.fsf@prism.localnet... > "Robert Klemme" writes: > > >> >> while ARGV.first =~ /^-/ > >> >> opt, arg = opts.get > >> >> case opt > >> >> when "--host" > >> >> config["host"] = arg > >> >> when "--port" > >> >> config["port"] = arg.to_i > >> >> end > >> >> end > > >> Not exactly. I need to stop parsing when a special command is seen. > >> "--host" and "--port" in the above example might appear in a different > >> order or not appear at all, so they're not reliable. > > > > Do you mean the problem is that you need to stop when a command is seen > > *and* this command can occur anywhere, i.e. also behind an option with an > > optional argument? > > Yes and no. Yes: this command can occur anywhere after one or more > options. No: I have no options with optional arguments so far, so I > haven't dealt with this case which I suspect to be more complex. Certainly. :-) > >> > Evaluate the first non option, this is your next command. > >> > >> This needs to go hand in hand with option parsing. Consider this: > >> > >> $ prog --opt1 arg1 --opt2 stuff --blah > >> > >> Is `stuff' a subcommand or opt2's argument? That depends on opt2's > >> definition (which in GetoptLong means NO_ARGUMENT or > >> REQUIRED_ARGUMENT). > > > > You have the same (or finer) level of control for option arguments in > > optparse, too. > > The kind of control I am trying to get was not given by GetoptLong per > se, but by the `while ARGV.first =~ /^-/', which breaks down parsing > in chunks where the subcommand can be detected. > > >> Then again, if you're thinking that things get > >> muddy when opt2 has an optional argument, I do agree... > > > > Yes, they certainly get muddy at this point. They get as muddy as to be > > undecidable if you have a command that is also a valid optional argument > > of an option. You can't decide what it is other than defining a priority > > for the resolution of this ambiguity. > > Yes. Or just disallowing optional arguments for generic options, the > need hasn't arisen so far and I doubt it will at least for this application. Yep, that works as well. > >> > Eat it and go to the first step only now remembering that options > >> > are stored for this command. Repeat until there are no more args > >> > left or the first non option is not a command (i.e. file name, > >> > ignorable or whatever). > >> > >> How is this different from the ARGV loop above? > > > > You have two nested loops, one of them is hidden in optparse. > > Sorry but I still can't make a picture of what you are suggesting. > Would you mind sketching a couple of lines of pseudocode? Hm... I'll attache a modified version of the optparse example which yields this output 10:55:10 [ruby]: ruby optparse-example-2.rb --verbose cmd1 -i foo 10:55:38 [ruby]: Of course, additional handling has to be done for file name arguments but I hope you get the picture. Kind regards robert ------=_NextPart_000_007A_01C4C581.B79561D0 Content-Type: application/octet-stream; name="optparse-example-2.rb" Content-Transfer-Encoding: quoted-printable Content-Disposition: attachment; filename="optparse-example-2.rb" #!/usr/bin/ruby=0A= =0A= require 'optparse'=0A= require 'optparse/time'=0A= require 'ostruct'=0A= require 'pp'=0A= =0A= ENV['POSIXLY_CORRECT']=3D'1'=0A= =0A= class OptparseExample=0A= =0A= CODES =3D %w[iso-2022-jp shift_jis euc-jp utf8 binary]=0A= CODE_ALIASES =3D { "jis" =3D> "iso-2022-jp", "sjis" =3D> "shift_jis" = }=0A= =0A= #=0A= # Return a structure describing the options.=0A= #=0A= def self.parse(args)=0A= # The options specified on the command line will be collected in = *options*.=0A= # We set default values here.=0A= options =3D OpenStruct.new=0A= options.library =3D []=0A= options.inplace =3D false=0A= options.encoding =3D "utf8"=0A= options.transfer_type =3D :auto=0A= options.verbose =3D false=0A= =0A= global_opts =3D OptionParser.new do |global_opts|=0A= global_opts.banner =3D "Usage: example.rb [options]"=0A= =0A= global_opts.separator ""=0A= global_opts.separator "Specific options:"=0A= =0A= # Mandatory argument.=0A= global_opts.on("-r", "--require LIBRARY",=0A= "Require the LIBRARY before executing your script") do = |lib|=0A= options.library << lib=0A= end=0A= =0A= # Optional argument; multi-line description.=0A= global_opts.on("-i", "--inplace [EXTENSION]",=0A= "Edit ARGV files in place",=0A= " (make backup if EXTENSION supplied)") do |ext|=0A= options.inplace =3D true=0A= options.extension =3D ext || ''=0A= options.extension.sub!(/\A\.?(?=3D.)/, ".") # Ensure = extension begins with dot.=0A= end=0A= =0A= # Cast 'delay' argument to a Float.=0A= global_opts.on("--delay N", Float, "Delay N seconds before = executing") do |n|=0A= options.delay =3D n=0A= end=0A= =0A= # Cast 'time' argument to a Time object.=0A= global_opts.on("-t", "--time [TIME]", Time, "Begin execution at = given time") do |time|=0A= options.time =3D time=0A= end=0A= =0A= # Cast to octal integer.=0A= global_opts.on("-F", "--irs [OCTAL]", OptionParser::OctalInteger,=0A= "Specify record separator (default \\0)") do |rs|=0A= options.record_separator =3D rs=0A= end=0A= =0A= # List of arguments.=0A= global_opts.on("--list x,y,z", Array, "Example 'list' of = arguments") do |list|=0A= options.list =3D list=0A= end=0A= =0A= # Keyword completion. We are specifying a specific set of = arguments (CODES=0A= # and CODE_ALIASES - notice the latter is a Hash), and the user = may provide=0A= # the shortest unambiguous text.=0A= code_list =3D (CODE_ALIASES.keys + CODES).join(',')=0A= global_opts.on("--code CODE", CODES, CODE_ALIASES, "Select = encoding",=0A= " (#{code_list})") do |encoding|=0A= options.encoding =3D encoding=0A= end=0A= =0A= # Optional argument with keyword completion.=0A= global_opts.on("--type [TYPE]", [:text, :binary, :auto],=0A= "Select transfer type (text, binary, auto)") do |t|=0A= options.transfer_type =3D t=0A= end=0A= =0A= # Boolean switch.=0A= global_opts.on("-v", "--[no-]verbose", "Run verbosely") do |v|=0A= options.verbose =3D v=0A= end=0A= =0A= global_opts.separator ""=0A= global_opts.separator "Common options:"=0A= =0A= # No argument, shows at tail. This will print an options = summary.=0A= # Try it and see!=0A= global_opts.on_tail("-h", "--help", "Show this message") do=0A= puts global_opts=0A= exit=0A= end=0A= =0A= # Another typical switch to print the version.=0A= global_opts.on_tail("--version", "Show version") do=0A= puts OptionParser::Version.join('.')=0A= exit=0A= end=0A= end=0A= =0A= cmd1_options =3D OpenStruct.new=0A= cmd1_options.library =3D []=0A= cmd1_options.inplace =3D false=0A= cmd1_options.encoding =3D "utf8"=0A= cmd1_options.transfer_type =3D :auto=0A= cmd1_options.verbose =3D false=0A= =0A= cmd1_opts =3D OptionParser.new do |cmd1_opts|=0A= cmd1_opts.banner =3D "Usage: example.rb [cmd1_options]"=0A= =0A= cmd1_opts.separator ""=0A= cmd1_opts.separator "Specific cmd1_options:"=0A= =0A= # Mandatory argument.=0A= cmd1_opts.on("-r", "--require LIBRARY",=0A= "Require the LIBRARY before executing your script") do = |lib|=0A= cmd1_options.library << lib=0A= end=0A= =0A= # Optional argument; multi-line description.=0A= cmd1_opts.on("-i", "--inplace [EXTENSION]",=0A= "Edit ARGV files in place",=0A= " (make backup if EXTENSION supplied)") do |ext|=0A= cmd1_options.inplace =3D true=0A= cmd1_options.extension =3D ext || ''=0A= cmd1_options.extension.sub!(/\A\.?(?=3D.)/, ".") # Ensure = extension begins with dot.=0A= end=0A= =0A= # Cast 'delay' argument to a Float.=0A= cmd1_opts.on("--delay N", Float, "Delay N seconds before = executing") do |n|=0A= cmd1_options.delay =3D n=0A= end=0A= =0A= # Cast 'time' argument to a Time object.=0A= cmd1_opts.on("-t", "--time [TIME]", Time, "Begin execution at = given time") do |time|=0A= cmd1_options.time =3D time=0A= end=0A= =0A= # Cast to octal integer.=0A= cmd1_opts.on("-F", "--irs [OCTAL]", OptionParser::OctalInteger,=0A= "Specify record separator (default \\0)") do |rs|=0A= cmd1_options.record_separator =3D rs=0A= end=0A= =0A= # List of arguments.=0A= cmd1_opts.on("--list x,y,z", Array, "Example 'list' of = arguments") do |list|=0A= cmd1_options.list =3D list=0A= end=0A= =0A= # Keyword completion. We are specifying a specific set of = arguments (CODES=0A= # and CODE_ALIASES - notice the latter is a Hash), and the user = may provide=0A= # the shortest unambiguous text.=0A= code_list =3D (CODE_ALIASES.keys + CODES).join(',')=0A= cmd1_opts.on("--code CODE", CODES, CODE_ALIASES, "Select = encoding",=0A= " (#{code_list})") do |encoding|=0A= cmd1_options.encoding =3D encoding=0A= end=0A= =0A= # Optional argument with keyword completion.=0A= cmd1_opts.on("--type [TYPE]", [:text, :binary, :auto],=0A= "Select transfer type (text, binary, auto)") do |t|=0A= cmd1_options.transfer_type =3D t=0A= end=0A= =0A= # Boolean switch.=0A= cmd1_opts.on("-v", "--[no-]verbose", "Run verbosely") do |v|=0A= cmd1_options.verbose =3D v=0A= end=0A= =0A= cmd1_opts.separator ""=0A= cmd1_opts.separator "Common cmd1_options:"=0A= =0A= # No argument, shows at tail. This will print an cmd1_options = summary.=0A= # Try it and see!=0A= cmd1_opts.on_tail("-h", "--help", "Show this message") do=0A= puts cmd1_opts=0A= exit=0A= end=0A= =0A= # Another typical switch to print the version.=0A= cmd1_opts.on_tail("--version", "Show version") do=0A= puts OptionParser::Version.join('.')=0A= exit=0A= end=0A= end=0A= =0A= global_opts.parse!(args)=0A= =0A= until args.empty?=0A= cmd =3D args.shift=0A= case cmd=0A= when "cmd1"=0A= cmd1_opts.parse!(args)=0A= else=0A= raise "Unknown command: #{cmd}"=0A= end=0A= end=0A= =0A= [options, cmd1_options]=0A= end # parse()=0A= =0A= end # class OptparseExample=0A= =0A= options, cmd1_options =3D OptparseExample.parse(ARGV)=0A= pp options=0A= pp cmd1_options=0A= =0A= ------=_NextPart_000_007A_01C4C581.B79561D0--