Analisi dell'opzione della riga di comando davvero economica in Ruby


114

EDIT: Per favore , per favore , per favore leggi i due requisiti elencati in fondo a questo post prima di rispondere. Le persone continuano a pubblicare le loro nuove gemme e librerie e quant'altro, che chiaramente non soddisfano i requisiti.

A volte voglio hackerare in modo molto economico alcune opzioni della riga di comando in un semplice script. Un modo divertente per farlo, senza occuparsi di getopts o parsing o qualcosa del genere, è:

...
$quiet       = ARGV.delete('-d')
$interactive = ARGV.delete('-i')
...
# Deal with ARGV as usual here, maybe using ARGF or whatever.

Non è proprio la normale sintassi delle opzioni Unix, perché accetterà parametri della riga di comando opzioni non opzioni, come in " myprog -i foo bar -q", ma posso conviverci. (Alcune persone, come gli sviluppatori di Subversion, preferiscono questo. A volte lo faccio anch'io.)

Un'opzione che è solo presente o assente non può essere implementata molto più semplicemente di quanto sopra. (Un compito, una chiamata di funzione, un effetto collaterale.) Esiste un modo altrettanto semplice per gestire le opzioni che accettano un parametro, come " -f nomefile "?

MODIFICARE:

Un punto che non ho fatto in precedenza, perché non mi era diventato chiaro fino a quando l'autore di Trollop ha menzionato che la libreria si adatta "in un file [800-line]", è che non sto cercando solo un file pulito sintassi, ma per una tecnica che ha le seguenti caratteristiche:

  1. L'intero codice può essere incluso nel file di script (senza sovraccaricare lo script stesso, che può essere solo un paio di dozzine di righe), in modo che si possa rilasciare un singolo file in una directory binsu qualsiasi sistema con uno standard Ruby 1.8 [5-7] installazione e utilizzarlo. Se non puoi scrivere uno script Ruby che non abbia istruzioni require e dove il codice per analizzare un paio di opzioni è inferiore a una dozzina di righe o giù di lì, fallisci questo requisito.

  2. Il codice è abbastanza piccolo e semplice da ricordarne abbastanza per digitare direttamente il codice che farà il trucco, piuttosto che tagliare e incollare da qualche altra parte. Pensa alla situazione in cui ti trovi sulla console di un server protetto da firewall senza accesso a Internet e desideri creare uno script rapido da utilizzare per un client. Non so voi, ma (oltre a non soddisfare il requisito di cui sopra) memorizzare anche le 45 righe di micro-optparse semplificato non è qualcosa che mi interessa fare.


2
Sei solo curioso dell'obiezione contro getoptlong?
Mark Carey

La verbosità di esso. Con getoptlog, a volte il codice di analisi delle opzioni è più lungo della parte dello script che esegue effettivamente il lavoro. Questo non è solo un problema estetico, ma un problema di costi di manutenzione.
cjs

8
Non capisco il requisito di inclusione dello script - entrambi getoptlonge optparsesono nella libreria ruby ​​standard, quindi non DEVI copiarli durante la distribuzione del tuo script - se ruby ​​funziona su quella macchina, allora require 'optparse'o require 'getoptlong'funzionerà anche.
rampion

Vedi stackoverflow.com/questions/21357953/… , così come la risposta di William Morgan di seguito su Trollop.
fearless_fool

@CurtSampson Non posso credere a quante persone non hanno risposto alla tua domanda. Ad ogni modo, finalmente ho ottenuto una buona risposta su 3 post in meno XD XD
OneChillDude

Risposte:


235

Come autore di Trollop , non posso CREDERE alle cose che le persone pensano siano ragionevoli in un parser di opzioni. Sul serio. Sconcerta la mente.

Perché dovrei creare un modulo che estenda qualche altro modulo per analizzare le opzioni? Perché dovrei sottoclassare qualcosa? Perché dovrei iscrivermi a qualche "framework" solo per analizzare la riga di comando?

Ecco la versione Trollop di quanto sopra:

opts = Trollop::options do
  opt :quiet, "Use minimal output", :short => 'q'
  opt :interactive, "Be interactive"
  opt :filename, "File to process", :type => String
end

E questo è tutto. optsè ora un hash con i tasti :quiet, :interactivee :filename. Puoi fare quello che vuoi con esso. E ottieni una bellissima pagina di aiuto, formattata per adattarsi alla larghezza dello schermo, nomi di argomenti brevi automatici, controllo del tipo ... tutto ciò di cui hai bisogno.

È un file, quindi puoi rilasciarlo nella tua directory lib / se non vuoi una dipendenza formale. Ha un DSL minimo che è facile da capire.

LOC per opzione persone. Importa.


39
A proposito, +1 per aver scritto Trollop (che era già stato menzionato qui), ma sentiti libero di attenuare un po 'il primo paragrafo.
cjs

33
Ha il diritto di lamentarsi in questo caso, temo. Quando guardi le alternative: [1 ] [2 ] [3 ], per quello che fondamentalmente sta solo elaborando un semplice array di stringhe (no davvero, lascia che penetri), non puoi fare a meno di chiederti PERCHÉ? Cosa guadagni da tutto quel gonfio? Questo non è C, dove sono le stringhe, "problematico". Ovviamente a ciascuno il suo. :)
srcspider

50
Per favore, non attenuarlo un po '. È un retto limite, fratello.
William Pietri

7
Sentiti libero di attenuare un po 'la decima parola.
Andrew Grimm

3
+1 per Trollop. Lo uso per il mio sistema di automazione dei test e funziona. Inoltre è così facile codificare con che a volte riorganizzo il mio banner solo per provarne la gioia.
kinofrost

76

Condivido il tuo disgusto per require 'getopts', principalmente a causa della suggestione che è OptionParser:

% cat temp.rb                                                            
require 'optparse'
OptionParser.new do |o|
  o.on('-d') { |b| $quiet = b }
  o.on('-i') { |b| $interactive = b }
  o.on('-f FILENAME') { |filename| $filename = filename }
  o.on('-h') { puts o; exit }
  o.parse!
end
p :quiet => $quiet, :interactive => $interactive, :filename => $filename
% ruby temp.rb                                                           
{:interactive=>nil, :filename=>nil, :quiet=>nil}
% ruby temp.rb -h                                                        
Usage: temp [options]
    -d
    -i
    -f FILENAME
    -h
% ruby temp.rb -d                                                        
{:interactive=>nil, :filename=>nil, :quiet=>true}
% ruby temp.rb -i                                                        
{:interactive=>true, :filename=>nil, :quiet=>nil}
% ruby temp.rb -di                                                       
{:interactive=>true, :filename=>nil, :quiet=>true}
% ruby temp.rb -dif apelad                                               
{:interactive=>true, :filename=>"apelad", :quiet=>true}
% ruby temp.rb -f apelad -i                                              
{:interactive=>true, :filename=>"apelad", :quiet=>nil}

6
Grazie, non riesco a vedere come questo non si adatti alla richiesta degli OP, soprattutto considerando che è tutto nella libreria standard, rispetto alla necessità di installare / vendere qualsiasi codice non standard
dolzenko

3
questo sembra proprio come la versione trollop tranne per il fatto che non necessita del file aggiuntivo.
Claudiu

59

Ecco la tecnica standard che utilizzo di solito:

#!/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}")

3
Risponde alla domanda, ma amico, Trollop sembra essere molto più facile da affrontare. Perché reinventare la ruota quando la ruota premade è molto più liscia?
Mikey TK

7
La ruota premade non è più liscia. Leggi di nuovo la domanda attentamente, prestando molta attenzione ai requisiti.
cjs

2
+1 A volte devi reinventare la ruota, perché non vuoi o semplicemente non puoi usare altre dipendenze come Trollop.
lzap

Trollop non ha bisogno di essere installato come una gemma. Puoi semplicemente rilasciare un file nella tua libcartella o codice e usarlo senza nemmeno toccare rubygems.
Overbryd

Per quanto mi riguarda, ho dovuto cambiare when /^-/ then usage("Unknown option: #{ARGV[0].inspect}")per when /^-/ then usage("Unknown option: #{ARGV.shift.inspect}")o sarebbe entrare in un ciclo infinito utilizzo
casey

36

Dal momento che nessuno sembrava menzionarlo e il titolo si riferisce all'analisi economica della riga di comando, perché non lasciare che l'interprete Ruby faccia il lavoro per te? Se passi l' -sinterruttore (nel tuo shebang, ad esempio), ottieni interruttori semplici e sporchi gratuitamente, assegnati a variabili globali di una sola lettera. Ecco il tuo esempio utilizzando questo interruttore:

#!/usr/bin/env ruby -s
puts "#$0: Quiet=#$q Interactive=#$i, ARGV=#{ARGV.inspect}"

Ed ecco l'output quando lo salvo come ./teste lo chmod +x:

$ ./test
./test: Quiet= Interactive=, ARGV=[]
$ ./test -q foo
./test: Quiet=true Interactive=, ARGV=["foo"]
$ ./test -q -i foo bar baz
./test: Quiet=true Interactive=true, ARGV=["foo", "bar", "baz"]
$ ./test -q=very foo
./test: Quiet=very Interactive=, ARGV=["foo"]

Vedere ruby -hper i dettagli.

Questo deve essere a buon mercato come si arriva. Solleverà un'eccezione NameError se provi un interruttore come -:, quindi c'è qualche convalida lì. Ovviamente, non puoi avere alcun interruttore dopo un argomento non-switch, ma se hai bisogno di qualcosa di stravagante, dovresti davvero usare come minimo OptionParser. In effetti, l'unica cosa che mi infastidisce di questa tecnica è che riceverai un avviso (se li hai abilitati) quando accedi a una variabile globale non impostata, ma è ancora falsa, quindi funziona bene per strumenti usa e getta e veloce script.

Un avvertimento sottolineato da FelipeC nei commenti in " Come eseguire un'analisi delle opzioni della riga di comando davvero economica in Ruby ", è che la tua shell potrebbe non supportare lo shebang a 3 token; potresti dover sostituire /usr/bin/env ruby -wcon il percorso effettivo del tuo ruby ​​(come /usr/local/bin/ruby -w), o eseguirlo da uno script wrapper o qualcosa del genere.


2
Grazie :) Spero proprio che non abbia aspettato questa risposta negli ultimi due anni.
DarkHeart

3
Ho davvero aspettato questa risposta negli ultimi due anni. :-) Più seriamente, questo è il tipo di pensiero intelligente che stavo cercando. L'avvertimento è un po 'fastidioso, ma posso pensare a come mitigarlo.
cjs

Sono contento di aver potuto (eventualmente) aiutare, @CurtSampson, Le bandiere della risonanza magnetica vengono strappate direttamente da Perl, dove tendono ad essere usate gratuitamente nelle battute di shell. Sentiti libero di accettare, se la risposta ti è ancora utile. :)
bjjb

1
Non puoi usare più argomenti in uno shebang in Linux. / usr / bin / env: 'ruby -s': nessun file o directory di questo tipo
FelipeC

13

Ho creato micro-optparse per soddisfare questa ovvia esigenza di un analizzatore di opzioni breve ma facile da usare. Ha una sintassi simile a Trollop ed è breve di 70 righe. Se non hai bisogno di convalide e puoi fare a meno delle righe vuote, puoi ridurlo a 45 righe. Penso che sia esattamente quello che stavi cercando.

Breve esempio:

options = Parser.new do |p|
  p.version = "fancy script version 1.0"
  p.option :verbose, "turn on verbose mode"
  p.option :number_of_chairs, "defines how many chairs are in the classroom", :default => 1
  p.option :room_number, "select room number", :default => 2, :value_in_set => [1,2,3,4]
end.process!

Chiamando lo script con -ho --helpverrà stampato

Usage: micro-optparse-example [options]
    -v, --[no-]verbose               turn on verbose mode
    -n, --number-of-chairs 1         defines how many chairs are in the classroom
    -r, --room-number 2              select room number
    -h, --help                       Show this message
    -V, --version                    Print version

Controlla se l'input è dello stesso tipo del valore predefinito, genera funzioni di accesso brevi e lunghe, stampa messaggi di errore descrittivi se vengono forniti argomenti non validi e altro ancora.

Ho confrontato diversi analizzatori di opzioni utilizzando ogni analizzatore di opzioni per il problema che avevo. Puoi utilizzare questi esempi e il mio riepilogo per prendere una decisione informativa. Sentiti libero di aggiungere altre implementazioni all'elenco. :)


La libreria stessa sembra che potrebbe essere eccezionale. Tuttavia, non è falso confrontare i conteggi delle righe con Trollop poiché si dipende e si richiede optparseche siano 1937 righe (dare o ricevere).
Telemaco

6
Il confronto dei conteggi delle righe è assolutamente OK, poiché optparseè una libreria predefinita, cioè viene fornita con ogni installazione di ruby. Trollopè una libreria di terze parti, quindi devi importare il codice completo ogni volta che vuoi includerlo in un progetto. µ-optparse richiede sempre solo le ~ 70 righe, poiché optparseè già presente.
Florian Pilz

8

Capisco perfettamente perché vuoi evitare optparse: può ottenere troppo. Ma ci sono alcune soluzioni molto "più leggere" (rispetto a OptParse) che vengono fornite come librerie ma sono abbastanza semplici da rendere utile l'installazione di una singola gemma.

Ad esempio, controlla questo esempio OptiFlag . Poche righe per l'elaborazione. Un esempio leggermente troncato su misura per il tuo caso:

require 'optiflag'

module Whatever extend OptiFlagSet
  flag "f"
  and_process!
end 

ARGV.flags.f # => .. whatever ..

Ci sono anche tantissimi esempi personalizzati . Ricordo di averne usato un altro che era ancora più semplice, ma per ora mi è sfuggito ma tornerò e aggiungerò un commento qui se lo trovo.


Sentiti libero di modificare la tua risposta per adattarla meglio alla domanda chiarita.
cjs

4

Questo è ciò che uso per argomenti davvero, davvero economici:

def main
  ARGV.each { |a| eval a }
end

main

quindi se esegui programname foo barchiama foo e poi bar. È utile per gli script usa e getta.


3

Puoi provare qualcosa come:

if( ARGV.include( '-f' ) )
  file = ARGV[ARGV.indexof( '-f' ) + 1 )]
  ARGV.delete('-f')
  ARGV.delete(file)
end

3

Hai considerato Thor di wycats? Penso che sia molto più pulito di optparse. Se hai già uno script scritto, potrebbe essere necessario un po 'di lavoro per formattarlo o rifattorizzarlo per thor, ma rende le opzioni di gestione molto semplici.

Ecco lo snippet di esempio dal README:

class MyApp < Thor                                                # [1]
  map "-L" => :list                                               # [2]

  desc "install APP_NAME", "install one of the available apps"    # [3]
  method_options :force => :boolean, :alias => :optional          # [4]
  def install(name)
    user_alias = options[:alias]
    if options.force?
      # do something
    end
    # ... other code ...
  end

  desc "list [SEARCH]", "list all of the available apps, limited by SEARCH"
  def list(search = "")
    # list everything
  end
end

Thor mappa automaticamente i comandi come tali:

app install myname --force

Viene convertito in:

MyApp.new.install("myname")
# with {'force' => true} as options hash
  1. Eredita da Thor per trasformare una classe in un mappatore di opzioni
  2. Mappare identificatori aggiuntivi non validi a metodi specifici. In questo caso, converti -L in: list
  3. Descrivi il metodo immediatamente sotto. Il primo parametro è le informazioni sull'utilizzo e il secondo parametro è la descrizione.
  4. Fornisci eventuali opzioni aggiuntive. Questi verranno gestiti da - e - parametri. In questo caso, viene aggiunta un'opzione --force e -f.

Mi piace la mappatura dei comandi, poiché un singolo binario con un mucchio di sottocomandi è qualcosa che faccio spesso. Tuttavia, anche se sei andato molto lontano dalla "luce". Potresti trovare un modo ancora più semplice per esprimere quella stessa funzionalità? E se non fosse necessario stampare l' --helpoutput? E se "head myprogram.rb" fosse l'output della guida?
cjs

3

Ecco il mio parser di opzioni rapido e sporco preferito:

case ARGV.join
when /-h/
  puts "help message"
  exit
when /-opt1/
  puts "running opt1"
end

Le opzioni sono espressioni regolari, quindi "-h" corrisponderà anche a "--help".

Leggibile, facile da ricordare, nessuna libreria esterna e codice minimo.


Si lo sarebbe. Se questo è un problema, puoi aggiungere più regex, ad esempio/-h(\b|elp)
EdwardTeach

2

Trollop è piuttosto economico.


Sarebbe < trollop.rubyforge.org >. Mi piace piuttosto, credo, anche se in realtà non stavo cercando una biblioteca.
cjs

È vero, è una biblioteca. Tuttavia, a <800 LOC, è piuttosto trascurabile. gitorious.org/trollop/mainline/blobs/master/lib/trollop.rb
g33kz0r

1
Stavo un po 'pensando che forse 30-50 righe sarebbero andate bene, se fossi arrivato al punto di usare una "libreria". Ma poi di nuovo, immagino che una volta ottenuto un file separato pieno di codice, la progettazione dell'API è più importante del conteggio delle righe. Tuttavia, non sono sicuro di volerlo includere in uno script una tantum che voglio semplicemente inserire nella directory bin su un sistema casuale.
cjs

1
Hai capito al contrario: il punto è evitare di dover avere una strategia di distribuzione più complessa.
cjs

1
Ti sbagli, perché stai interpretando (o ignorando) completamente i bisogni e le intenzioni della persona che ha posto la domanda. Le suggerisco di rileggere attentamente la domanda, in particolare gli ultimi due punti.
cjs

2

Se desideri un semplice parser da riga di comando per comandi chiave / valore senza l'uso di gemme:

Ma funziona solo se hai sempre coppie chiave / valore.

# example
# script.rb -u username -p mypass

# check if there are even set of params given
if ARGV.count.odd? 
    puts 'invalid number of arguments'
    exit 1
end

# holds key/value pair of cl params {key1 => value1, key2 => valye2, ...}
opts = {} 

(ARGV.count/2).times do |i|
    k,v = ARGV.shift(2)
    opts[k] = v # create k/v pair
end

# set defaults if no params are given
opts['-u'] ||= 'root'

# example use of opts
puts "username:#{opts['-u']} password:#{opts['-p']}"

Se non hai bisogno di alcun controllo puoi semplicemente usare:

opts = {} 

(ARGV.count/2).times do |i|
    k,v = ARGV.shift(2)
    opts[k] = v # create k/v pair
end

2

Ecco lo snippet di codice che utilizzo nella parte superiore della maggior parte dei miei script:

arghash = Hash.new.tap { |h| # Parse ARGV into a hash
    i = -1                      
    ARGV.map{  |s| /(-[a-zA-Z_-])?([^=]+)?(=)?(.+)?/m.match(s).to_a }
     .each{ |(_,a,b,c,d)| h[ a ? "#{a}#{b}#{c}" : (i+=1) ] =
                             (a ? (c ? "#{d}" : true) : "#{b}#{c}#{d}") 
          }
    [[:argc,Proc.new  {|| h.count{|(k,_)| !k.is_a?(String)}}],
     [:switches, Proc.new {|| h.keys.select{|k| k[0] == '-' }}]
    ].each{|(n,p)| h.define_singleton_method(n,&p) }
}

Odio anche richiedere file aggiuntivi nei miei script veloci e sporchi. La mia soluzione è molto simile a quella che stai chiedendo. Incollo uno snippet di codice di 10 righe all'inizio di uno qualsiasi dei miei script che analizza la riga di comando e inserisce gli argomenti posizionali e gli interruttori in un oggetto Hash (di solito assegnato a un oggetto che ho chiamato arghash negli esempi seguenti).

Ecco un esempio di riga di comando che potresti voler analizzare ...

./myexampleprog.rb -s -x=15 --longswitch arg1 --longswitch2=val1 arg2

Che diventerebbe un hash come questo.

 { 
   '-s' => true, 
   '-x=' => '15', 
   '--longswitch' => true, 
   '--longswitch2=' => 'val1', 
   0 => 'arg1', 
   1 => 'arg2'
 }

Oltre a ciò, all'hash vengono aggiunti due metodi di convenienza:

  • argc() restituirà il conteggio degli argomenti non di commutazione.
  • switches() restituirà un array contenente le chiavi per gli interruttori presenti

Questo significa consentire alcune cose veloci e sporche come ...

  • Convalida che ho il giusto numero di argomenti posizionali indipendentemente dalle opzioni passate in ( arghash.argc == 2)
  • Accedi agli argomenti posizionali in base alla loro posizione relativa, indipendentemente dagli interruttori che compaiono prima o intervallati da argomenti posizionali (ad es. arghash[1]Ottiene sempre il secondo argomento non interruttore).
  • Supporta opzioni assegnate a valore nella riga di comando come "--max = 15" a cui è possibile accedere e arghash['--max=']che restituisce un valore di "15" data la riga di comando di esempio.
  • Verifica la presenza o l'assenza di un'opzione nella riga di comando utilizzando una notazione molto semplice come la arghash['-s']quale restituisce true se è presente e nullo se è assente.
  • Verifica la presenza di un interruttore o alternative di interruttori utilizzando operazioni di impostazione come

    puts USAGETEXT if !(%w(-h --help) & arghash.switches()).empty?

  • Identificare l'uso di interruttori non validi utilizzando operazioni di impostazione come

    puts "Invalid switch found!" if !(arghash.switches - %w(-valid1 -valid2)).empty?

  • Specificare i valori predefiniti per gli argomenti mancanti utilizzando un semplice Hash.merge()come nell'esempio seguente che inserisce un valore per -max = se non è stato impostato e aggiunge un quarto argomento posizionale se non è stato passato.

    with_defaults = {'-max=' => 20, 3 => 'default.txt'}.merge(arghash)


(L'ho modificato per migliorare la formattazione del codice, principalmente usando l'allineamento per rendere più chiaro il blocco e la struttura di controllo, che ritengo sia particolarmente importante in qualcosa di così denso di punteggiatura. Ma se odi la nuova formattazione, sentiti libero per annullare la modifica.)
cjs

Questa è piuttosto carina, se non la cosa più facile da leggere al mondo. Mi piace che dimostri anche che cambiare la "sintassi" degli argomenti (qui, consentire le opzioni dopo gli argomenti posizionali e non consentire gli argomenti delle opzioni tranne che usando =) può fare la differenza nel codice di cui hai bisogno.
cjs

Grazie per la riformattazione. È decisamente oscuro da leggere e si potrebbe facilmente scambiare la lunghezza del codice per la chiarezza. Ora che mi fido di questo codice, più o meno, lo tratto come un gioiello e non cerco mai di capire cosa sta facendo sotto le coperte (quindi la chiarezza non è più importante ora che ho fiducia).
David Foster

1

Questo è molto simile alla risposta accettata, ma usando ARGV.delete_ifquello che uso nel mio semplice parser . L'unica vera differenza è che le opzioni con argomenti devono essere insieme (ad esempio -l=file).

def usage
  "usage: #{File.basename($0)}: [-l=<logfile>] [-q] file ..."
end

$quiet = false
$logfile = nil

ARGV.delete_if do |cur|
  next false if cur[0] != '-'
  case cur
  when '-q'
    $quiet = true
  when /^-l=(.+)$/
    $logfile = $1
  else
    $stderr.puts "Unknown option: #{cur}"
    $stderr.puts usage
    exit 1
  end
end

puts "quiet: #{$quiet} logfile: #{$logfile.inspect} args: #{ARGV.inspect}"

0

Apparentemente io e WilliamMorgan la pensiamo allo stesso modo. Ho appena rilasciato la scorsa notte su Github quella che ora vedo è una libreria simile a Trollop (Chiamato come?) Dopo aver fatto una ricerca per OptionParser su Github, vedi Switch

Ci sono alcune differenze, ma la filosofia è la stessa. Una differenza evidente è che Switches dipende da OptionParser.


0

Sto sviluppando la mia gemma parser di opzioni chiamata Acclaim .

L'ho scritto perché volevo creare interfacce a riga di comando in stile git ed essere in grado di separare in modo pulito la funzionalità di ciascun comando in classi separate, ma può anche essere utilizzato senza l'intero framework di comando:

(options = []) << Acclaim::Option.new(:verbose, '-v', '--verbose')
values = Acclaim::Option::Parser.new(ARGV, options).parse!
puts 'Verbose.' if values.verbose?

Nessuna versione stabile al momento, ma ho già implementato alcune funzionalità come:

  • parser di opzioni personalizzato
  • analisi flessibile degli argomenti dell'opzione che consente sia il minimo che l'opzionale
  • supporto per molti stili di opzioni
  • sostituire, aggiungere o aumentare in più istanze della stessa opzione
  • gestori di opzioni personalizzati
  • gestori di tipi personalizzati
  • gestori predefiniti per le classi di libreria standard comuni

C'è molta enfasi sui comandi, quindi potrebbe essere un po 'pesante per la semplice analisi della riga di comando, ma funziona bene e l'ho usato su tutti i miei progetti. Se sei interessato all'aspetto dell'interfaccia di comando, dai un'occhiata la pagina GitHub del progetto per ulteriori informazioni ed esempi.


1
Consiglio vivamente Acclaim. È facile da usare e ha tutte le opzioni di cui hai bisogno.
bowsersenior

0

Supponiamo che un comando abbia al massimo un'azione e un numero arbitrario di opzioni come questa:

cmd.rb
cmd.rb action
cmd.rb action -a -b ...
cmd.rb action -ab ...

L'analisi senza convalida può essere così:

ACTION = ARGV.shift
OPTIONS = ARGV.join.tr('-', '')

if ACTION == '***'
  ...
  if OPTIONS.include? '*'
    ...
  end
  ...
end

0

https://github.com/soveran/clap

other_args = Clap.run ARGV,
  "-s" => lambda { |s| switch = s },
  "-o" => lambda { other = true }

46LOC (a 1.0.0), nessuna dipendenza dal parser di opzioni esterno. Ottiene il lavoro fatto. Probabilmente non è completo come gli altri, ma è 46LOC.

Se controlli il codice, puoi facilmente duplicare la tecnica sottostante: assegna lambda e usa arity per assicurarti che il numero corretto di argomenti segua il flag se davvero non vuoi una libreria esterna.

Semplice. A buon mercato.


EDIT : il concetto sottostante si è riassunto in quanto suppongo che potresti copiarlo / incollarlo in uno script per creare un parser ragionevole della riga di comando. E 'sicuramente non è qualcosa che avrei impegnarsi a memoria, ma utilizzando l'arietà lambda come un parser a basso costo è una nuova idea:

flag = false
option = nil
opts = {
  "--flag" => ->() { flag = true },
  "--option" => ->(v) { option = v }
}

argv = ARGV
args = []

while argv.any?
  item = argv.shift
  flag = opts[item]

  if flag
    raise ArgumentError if argv.size < arity
    flag.call(*argv.shift(arity))
  else
    args << item
  end
end

# ...do stuff...

Si prega di leggere il punto 1 alla fine della domanda. Se non riesci a digitare tutto il codice necessario direttamente nella tua risposta qui, non è una risposta alla domanda.
cjs

Buon punto! Penso che al momento pensavo che la libreria fosse abbastanza piccola da poter copiare / incollare l'intera cosa in qualsiasi script su cui stavi lavorando senza bisogno di una dipendenza esterna, ma sicuramente non è una riga pulita che vorrei impegnare in memoria per soddisfare il tuo punto n. 2. Penso che il concetto di base sia nuovo e abbastanza interessante da essere andato avanti e ne ho fatto una versione ridotta che risponde alla tua domanda in modo un po 'più appropriato.
Ben Alavi

-1

Condividerò il mio semplice parser di opzioni su cui ho lavorato per un po 'di tempo. Sono solo 74 righe di codice e fa le basi di ciò che fa il parser di opzioni interno di Git. Ho preso OptionParser come ispirazione, e anche Git's.

https://gist.github.com/felipec/6772110

Assomiglia a questo:

opts = ParseOpt.new
opts.usage = "git foo"

opts.on("b", "bool", help: "Boolean") do |v|
 $bool = v
end

opts.on("s", "string", help: "String") do |v|
 $str = v
end

opts.on("n", "number", help: "Number") do |v|
 $num = v.to_i
end

opts.parse

Non hai nemmeno controllato il codice. Ho messo un'altra risposta eliminando il codice di analisi.
FelipeC

Non avevo bisogno di sapere che hai detto che era lungo 74 righe. Tuttavia, l'ho appena esaminato e viola ancora la prima frase del requisito 2. (Questa risposta viola anche la convenzione di Stack Overflow secondo cui dovresti includere il codice nella tua risposta piuttosto che fornire un collegamento esterno al sito.)
cjs

-1

EasyOptions non richiede alcuna opzione di analisi del codice. Basta scrivere il testo della guida, richiedere, fatto.

## Options:
##   -i, --interactive  Interactive mode
##   -q, --quiet        Silent mode

require 'easyoptions'
unless EasyOptions.options[:quiet]
    puts 'Interactive mode enabled' if EasyOptions.options[:interactive]
    EasyOptions.arguments.each { |item| puts "Argument: #{item}" }
end

EasyOptions è un singolo file Ruby senza istruzioni require e non c'è alcun codice di analisi da ricordare. Sembra invece che tu voglia qualcosa di incorporabile che sia abbastanza potente ma semplice da ricordare.
Renato Silva
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.