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