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
How to Recompile a Ruby with Rvm
How to Access a Ruby Module Method
Ruby: Get All Keys in a Hash (Including Sub Keys)
How to Use "Gets" on a Rake Task
Errno::Enoent: No Such File or Directory Ruby
Rails 3.2.2 (Or 3.2.1) + Postgresql 9.1.3 + Ubuntu 11.10 Connection Error
Ruby Merging Two Arrays into One
Token Based Authentication for Rails JSON APIs
Ruby: Select a Hash from Inside an Array
What Is Double Method in Rspec For
Rails: How to Show User's "Last Seen At" Time
All Possible Permutations of a Given String
Cleanest Way to Create a Hash from an Array
How to Delete All Contents of a Folder with Ruby-Rails
Paginate Multiple Models in Kaminari
What's the Difference Between a Class and the Singleton of That Class in Ruby