How to use different options for every positional argument with OptionParser

786 views Asked by At

I want to make a script invoked with this syntax:

script.rb [common-options] arg1 [arg1-options] [arg2 [arg2-options] …]

For example:

script.rb --verbose --dry-run a1 --outfile c1 a2 --reprocess --outfile c2 a3 a4 --outfile b42

Returning something like this to my code:

options = {verbose: true, :'dry-run' => true}
args = {
  'a1' => {outfile: 'c1'},
  'a2' => {outfile: 'c2', reprocess: true}, 
  'a3' => {},
  'a4' => {outfile: 'b42'},
}

I can't figure out from the OptionParser documentation whether it's possible at all.

Does anyone know a solution for this?

1

There are 1 answers

1
Envek On

Finally found solution in "Using ruby's OptionParser to parse sub-commands".

I need to use the poorly documented order method which will parse options only up to first positional argument. parse will parse the whole ARGV:

require 'optparse'

options = {}
filenames = []
file_options = Hash.new do |hash, key| hash[key] = {} end
filename = nil

file_parser = OptionParser.new do |o|
  o.banner = 'Options for every file (goes after every filename):'
  o.on '-o', '--outfile=FILE', 'A file to write result to' do |arg|
    file_options[filename][:outfile] = arg
  end
end

global_parser = OptionParser.new do |o|
  o.banner = [
    "Super duper file processor.\n",
    "Usage: #{$PROGRAM_NAME} [options] file1 [file1-options] [file2 [file2-options] …]",
  ].join("\n")
  o.separator ''
  o.separator 'Common options:'
  o.on '-v', '--verbose', 'Display result filenames' do |arg|
    options[:verbose] = arg
  end
  o.on '-h', '--help', 'Print this help and exit' do
    $stderr.puts opts
    exit
  end
  o.separator ''
  o.separator file_parser.help
end

begin
  argv = global_parser.order(ARGV)
  while (filename = argv.shift)
    filenames.push(filename)
    file_parser.order!(argv)
  end
rescue OptionParser::MissingArgument => e
  $stderr.puts e.message
  $stderr.puts global_parser
  exit 1
end

if filenames.empty?
  $stderr.puts global_parser
  exit 1
end

If I execute my script like this:

script.rb -v a -o a.out b c d -o d.out

I will get:

options      = {:verbose=>true}
file_options = {"a"=>{:outfile=>"a.out"}, "d"=>{:outfile=>"d.out"}}

And this help text will be generated:

Super duper file processor.

Usage: script.rb [options] file1 [file1-options] [file [file2-options]…]

Common options:
    -v, --verbose                    Display converted filenames
    -h, --help                       Print this help and exit

Options for every file (goes after every filename):
    -o, --outfile=FILE               A file to write result to