2010-04-28 7 views
22

ich analysieren möchte in der Lage sein, Rubys OptionParser verwenden Unterbefehle des FormularsRubys OptionParser Mit Unter Befehle

COMMAND [GLOBAL FLAGS] [SUB-COMMAND [SUB-COMMAND FLAGS]] 

wie zu analysieren:

git branch -a 
gem list foo 

Ich weiß, ich könnte wechseln zu einer anderen Optionsparser-Bibliothek (wie Trollop), aber ich bin daran interessiert zu lernen, wie man dies in OptionParser macht, da ich die Bibliothek besser lernen möchte.

Irgendwelche Tipps?

+0

Keine Tipps, abgesehen von einem Vorschlag, um Richtungen zu wechseln. Meiner Erfahrung nach war 'OptionParser' aus verschiedenen Gründen frustrierend. Einer davon ist die schlechte Dokumentation - daher Ihre Frage. William Morgan, der Autor von Trollop, zeigt keine Gnade in seiner Kritik (siehe zB http://stackoverflow.com/questions/897630/really-cheap-command-line-option-parsing-in-ruby und http://trollop.rubyforge.org). Ich kann nicht bestreiten, was er sagt. – FMc

+1

@FM: Nun, wie der Autor dieser Frage, ich bin auf einer Maschine stecken, wo das Importieren von Bibliotheken eine PITA ist, also versuche ich, mit den Standard-Bibliotheken - wie 'optparse' auszukommen. – rampion

Antwort

35

Ich habe es herausgefunden. Ich muss OptionParser#order! verwenden. Es wird alle Optionen von Anfang an ARGV analysieren, bis es eine Nicht-Option findet (das ist kein Optionsargument), alles, was es verarbeitet, aus ARGV entfernt, und dann wird es beendet.

Also muss ich tun, nur so etwas wie:

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! 
+2

Als Referenz finden Sie ein vollständigeres Beispiel in diesem [Gist] (https://gist.github.com/rkumar/445735). – sschuberth

+0

Was ist, wenn 'foo' und' baz' viele gemeinsame Optionen teilen? Wie vermeide ich Wiederholungen? –

+0

Fernando Á: einfach, nur die gemeinsamen Optionen zu einer Methode abstrahieren. 'def common_options (&blk) ; OptionParser.new {| opts | opts.on (...); ...; blk.call (opts)}; end', dann rufe diese Methode später mit einem Block für unterbefehlsspezifische Optionen auf - 'subcommands = {'foo' => allgemeine_optionen {| opts | ...}, 'baz' => allgemeine_optionen {| opts | ...}, ...}' – rampion

0

Es gibt auch andere Edelsteine ​​Sie wie main aussehen kann.

+0

hat das irgendwelche docs? – rampion

+0

@trampion: Sie können sich die Beispiele ansehen, zum Beispiel http://codeforpeople.com/lib/ruby/main/main-2.8.3/samples/f.rb –

1

Es sieht so aus, als ob die OptionParser-Syntax einige geändert hat. Ich musste folgendes verwenden, damit das arguments-Array alle Optionen hatte, die vom opts-Objekt nicht analysiert wurden.

begin 
    opts.order!(arguments) 
rescue OptionParser::InvalidOption => io 
    # Prepend the invalid option onto the arguments array 
    arguments = io.recover(arguments) 
rescue => e 
    raise "Argument parsing failed: #{e.to_s()}" 
end 
0

GLI ist die Art und Weise, zu gehen https://github.com/davetron5000/gli. Ein Auszug aus einem Tutorial:

#!/usr/bin/env ruby 
require 'gli' 
require 'hacer' 

include GLI::App 

program_desc 'A simple todo list' 

flag [:t,:tasklist], :default_value => File.join(ENV['HOME'],'.todolist') 

pre do |global_options,command,options,args| 
    $todo_list = Hacer::Todolist.new(global_options[:tasklist]) 
end 

command :add do |c| 
    c.action do |global_options,options,args| 
    $todo_list.create(args) 
    end 
end 

command :list do |c| 
    c.action do 
    $todo_list.list.each do |todo| 
     printf("%5d - %s\n",todo.todo_id,todo.text) 
    end 
    end 
end 

command :done do |c| 
    c.action do |global_options,options,args| 
    id = args.shift.to_i 
    $todo_list.list.each do |todo| 
     $todo_list.complete(todo) if todo.todo_id == id 
    end 
    end 
end 

exit run(ARGV) 

Sie können das Tutorial an http://davetron5000.github.io/gli/ finden.