Using Ruby's Optionparser to Parse Sub-Commands

Using ruby's OptionParser to parse sub-commands

Figured it out. I need to use OptionParser#order!. It will parse all the options from the start of ARGV until it finds a non-option (that isn't an option argument), removing everything it processes from ARGV, and then it will quit.

So I just need to do something like:

global = OptionParser.new do |opts|
# ...
end
subcommands = {
'foo' => OptionParser.new do |opts|
# ...
end,
# ...
'baz' => OptionParser.new do |opts|
# ...
end
}

global.order!
subcommands[ARGV.shift].order!

OptionParser with subcommands

The lines with the error:

global.order!
command = ARGV.shift
subcommands[command].order!

If global.order! uses all of ARGV, then command is nil. So... check for that.

global.order!
command = ARGV.shift
unless command
STDERR.puts "ERROR: no subcommand"
STDERR.puts global # prints usage
exit(-1)
end
subcommands[command].order!

Really Cheap Command-Line Option Parsing in Ruby

Here's the standard technique I usually use:

#!/usr/bin/env ruby

def usage(s)
$stderr.puts(s)
$stderr.puts("Usage: #{File.basename($0)}: [-l <logfile] [-q] file ...")
exit(2)
end

$quiet = false
$logfile = nil

loop { case ARGV[0]
when '-q' then ARGV.shift; $quiet = true
when '-l' then ARGV.shift; $logfile = ARGV.shift
when /^-/ then usage("Unknown option: #{ARGV[0].inspect}")
else break
end; }

# Program carries on here.
puts("quiet: #{$quiet} logfile: #{$logfile.inspect} args: #{ARGV.inspect}")

Ruby OptionParser: how to handle arguments without a prefix (like a required filename)

OptionParser specifically handles options - that is, things starting with dashes. After it parses, the remaining arguments are left in ARGV. You can check for your filename there and exit with an error if it's missing.

With a slight modification on their minimal example,

require 'optparse'

options = {}
OptionParser.new do |opts|
opts.banner = "Usage: example.rb [options]"

opts.on("-v", "--[no-]verbose", "Run verbosely") do |v|
options[:verbose] = v
end
end.parse!

p options
p ARGV
p "Where is my hat?!" if ARGV.length == 0

You get this:

$ ruby parse.rb
{}
[]
"Where is my hat?!"

$ ruby parse.rb hat
{}
["hat"]

$ ruby parse.rb -v hat
{:verbose=>true}
["hat"]

OptionParser to parse arguments form file instead of command line

The parse! method wants an array as its argument, not a string. You'll probable want to use Shellwords.shellsplit rather than String#split (or similar hand-rolled method) to convert your line to an array just in case you have to deal with quoting and whatnot. Something like this:

OptionParser.new do |opts|
#...
end.parse!(Shellwords.shellsplit(line))

How to use variable arguments with ruby's OptionParser

OPTS = {}

op = OptionParser.new do |x|
x.banner = 'cat <options> <file>'
x.separator ''

x.on("-A", "--show-all", "Equivalent to -vET")
{ OPTS[:showall] = true }

x.on("-b", "--number-nonblank", "number nonempty output lines")
{ OPTS[:number_nonblank] = true }

x.on("-x", "--start-from NUM", Integer, "Start numbering from NUM")
{ |n| OPTS[:start_num] = n }

x.on("-h", "--help", "Show this message")
{ puts op; exit }

end

op.parse!(ARGV)

# Example code for dealing with filenames
ARGV.each{ |fn| output_file(OPTS, fn) }

I shall leave other command line operations, as they say, as an exercise for the reader! You get the idea.

(NB: I had to invent a fictional -x parameter to demo passing a value after a flag.)

Update: I should have explained that this will leave ARGV as an array of filenames, assuming that the user has entered any.

How can I configure optparse to accept both parameter options as a command?

Command-line arguments(not options) are in ARGV after calling OptionParser#parse! because #parse! extracts options from ARGV.
So, you can get subcommand like this:

options = {}

OptionParser.new do |opts|
# definitions of command-line options...
# ...
end.parse!

subcommand = ARGV.shift || "init"

print "options: "
p options
puts "subcommand: #{subcommand}"

If you have many subcommands, Thor gem might help you.

And, although this is not answer for your question, brackets([]) in option definition mean that the option's argument is optional.
For instance, at your definitions, email and password might be nil even when the options are passed:

$ pivotal_commit -e
options: {:email=>nil}
subcommand: init

If you require argument when the option is passed, remove brackets:

# ...
opts.on("-e", "--email EMAIL", String, "The email to the PT account you want to access") do |v|
options[:email] = v
end
# ...

Now argument for email is required:

$ pivotal_commit -e
pivotal_commit:6:in `<main>': missing argument: -e (OptionParser::MissingArgument)


Related Topics



Leave a reply



Submit