2014-10-17 19 views
31

Voglio chiamare uno script Ruby dalla riga di comando e passare i parametri che sono coppie chiave/valore. linea di chiamataAnalizza gli argomenti della riga di comando in uno script Ruby

Comando:

$ ruby my_script.rb --first_name=donald --last_name=knuth 

my_script.rb:

puts args.first_name + args.last_name 

Qual è il modo standard di Ruby per fare questo? In altre lingue di solito devo usare un parser di opzioni. In Ruby ho visto che abbiamo ARGF.read, ma non sembra funzionare coppie chiave/valore come in questo esempio.

OptionParser sembra promettente, ma non posso dire se effettivamente supporta questo caso.

+0

https://www.ruby-toolbox.com/categories/CLI_Option_Parsers –

+0

Se non sbaglio, Highline sembra l funzioni di aiuto ike per chiedere agli utenti di inserire Quindi userei Highline per far dire alla mia console "First Name:" e attendo il loro input. C'è una funzione particolare che dovrei guardare in essa? –

+1

Ci sono molte gemme tra cui puoi scegliere; quel sito web classifica le biblioteche e le ordina in base alla popolarità. Ho persino scritto la mia gemma, chiamata "acclaim", e supporta la sintassi '--option = value'. Non ho avuto il tempo di mantenere i miei progetti di software libero, però. Dovresti scegliere una libreria meglio supportata. –

risposta

23

Sulla base della risposta di @MartinCortez, ecco una breve parte che fa un hash di coppie chiave/valore, in cui i valori devono essere uniti con un segno =. Supporta anche argomenti di bandiera senza valori:

args = Hash[ ARGV.join(' ').scan(/--?([^=\s]+)(?:=(\S+))?/) ] 

... o, in alternativa ...

args = Hash[ ARGV.flat_map{|s| s.scan(/--?([^=\s]+)(?:=(\S+))?/) } ] 

Chiamato con -x=foo -h --jim=jam torna {"x"=>"foo", "h"=>nil, "jim"=>"jam"} in modo da poter fare le cose come:

puts args['jim'] if args.key?('h') 
#=> jam 

Mentre ci sono più librerie per gestire questo, incluso GetoptLong included with Ruby-Personalmente preferisco rotolare da solo. Ecco il modello che uso, che lo rende ragionevolmente generica, non è legato ad un formato di utilizzo specifico, e sufficientemente flessibile per consentire bandiere mescolate, le opzioni e gli argomenti richiesti in vari ordini:

USAGE = <<ENDUSAGE 
Usage: 
    docubot [-h] [-v] [create [-s shell] [-f]] directory [-w writer] [-o output_file] [-n] [-l log_file] 
ENDUSAGE 

HELP = <<ENDHELP 
    -h, --help  Show this help. 
    -v, --version Show the version number (#{DocuBot::VERSION}). 
    create   Create a starter directory filled with example files; 
        also copies the template for easy modification, if desired. 
    -s, --shell  The shell to copy from. 
        Available shells: #{DocuBot::SHELLS.join(', ')} 
    -f, --force  Force create over an existing directory, 
        deleting any existing files. 
    -w, --writer  The output type to create [Defaults to 'chm'] 
        Available writers: #{DocuBot::Writer::INSTALLED_WRITERS.join(', ')} 
    -o, --output  The file or folder (depending on the writer) to create. 
        [Default value depends on the writer chosen.] 
    -n, --nopreview Disable automatic preview of .chm. 
    -l, --logfile Specify the filename to log to. 

ENDHELP 

ARGS = { :shell=>'default', :writer=>'chm' } # Setting default values 
UNFLAGGED_ARGS = [ :directory ]    # Bare arguments (no flag) 
next_arg = UNFLAGGED_ARGS.first 
ARGV.each do |arg| 
    case arg 
    when '-h','--help'  then ARGS[:help]  = true 
    when 'create'   then ARGS[:create] = true 
    when '-f','--force'  then ARGS[:force]  = true 
    when '-n','--nopreview' then ARGS[:nopreview] = true 
    when '-v','--version' then ARGS[:version] = true 
    when '-s','--shell'  then next_arg = :shell 
    when '-w','--writer' then next_arg = :writer 
    when '-o','--output' then next_arg = :output 
    when '-l','--logfile' then next_arg = :logfile 
    else 
     if next_arg 
     ARGS[next_arg] = arg 
     UNFLAGGED_ARGS.delete(next_arg) 
     end 
     next_arg = UNFLAGGED_ARGS.first 
    end 
end 

puts "DocuBot v#{DocuBot::VERSION}" if ARGS[:version] 

if ARGS[:help] or !ARGS[:directory] 
    puts USAGE unless ARGS[:version] 
    puts HELP if ARGS[:help] 
    exit 
end 

if ARGS[:logfile] 
    $stdout.reopen(ARGS[:logfile], "w") 
    $stdout.sync = true 
    $stderr.reopen($stdout) 
end 

# etc. 
+0

Questo è bello, ma se si sta usando anche l'ARGF 'incorporato per leggere i flussi tramite nomi file/stdin è necessario assicurarsi di consumare gli argomenti da' ARGV' prima di usare 'ARGF.read' o si finire con errori "Nessun file o directory". – Lauren

2

Un po 'di standard di Rubino Regexp in myscript.rb:

args = {} 

ARGV.each do |arg| 
    match = /--(?<key>.*?)=(?<value>.*)/.match(arg) 
    args[match[:key]] = match[:value] # e.g. args['first_name'] = 'donald' 
end 

puts args['first_name'] + ' ' + args['last_name'] 

E sulla linea di comando:

$ ruby script.rb --first_name=donald --last_name=knuth

produce:

$ donald knuth

+1

È carino! Il requisito di usare '=' per disambiguare tra '-f foo' e' -x -y bar' è un po 'di antipasto per l'utilizzo nel mondo reale per me, ma è un bel trucco veloce. – Phrogz

+1

Vero. Stavo solo seguendo il suo formato sopra. Sulla base della domanda, non sono sicuro se si preoccupa della disambiguazione. –

+0

Sembra fantastico ma come definiresti 's'? 'ARGV.join.to_s'? –

72

Ruby built-in OptionParser fa questo bene. Combinate con OpenStruct e sei a casa gratuitamente:

require 'optparse' 

options = {} 
OptionParser.new do |opt| 
    opt.on('--first_name FIRSTNAME') { |o| options[:first_name] = o } 
    opt.on('--last_name LASTNAME') { |o| options[:last_name] = o } 
end.parse! 

puts options 

options conterrà i parametri ei valori come un hash.

risparmio e l'esecuzione che dalla riga di comando senza parametri risultati in:

$ ruby test.rb 
{} 

eseguirlo con i parametri:

$ ruby test.rb --first_name=foo --last_name=bar 
{:first_name=>"foo", :last_name=>"bar"} 

Questo esempio sta usando un hash per contenere le opzioni, ma si può utilizzare un OpenStruct che si tradurrà in uso come la vostra richiesta:

require 'optparse' 
require 'ostruct' 

options = OpenStruct.new 
OptionParser.new do |opt| 
    opt.on('-f', '--first_name FIRSTNAME', 'The first name') { |o| options.first_name = o } 
    opt.on('-l', '--last_name LASTNAME', 'The last name') { |o| options.last_name = o } 
end.parse! 

puts options.first_name + ' ' + options.last_name 

$ ruby test.rb --first_name=foo --last_name=bar 
foo bar 

I t crea anche automaticamente il vostro -h o --help opzione:

$ ruby test.rb -h 
Usage: test [options] 
     --first_name FIRSTNAME 
     --last_name LASTNAME 

È possibile utilizzare contrassegni brevi troppo:

require 'optparse' 

options = {} 
OptionParser.new do |opt| 
    opt.on('-f', '--first_name FIRSTNAME') { |o| options[:first_name] = o } 
    opt.on('-l', '--last_name LASTNAME') { |o| options[:last_name] = o } 
end.parse! 

puts options 

esecuzione che attraverso i suoi passi:

$ ruby test.rb -h 
Usage: test [options] 
    -f, --first_name FIRSTNAME 
    -l, --last_name LASTNAME 
$ ruby test.rb -f foo --l bar 
{:first_name=>"foo", :last_name=>"bar"} 

E 'facile aggiungere spiegazioni in linea anche per le opzioni:

OptionParser.new do |opt| 
    opt.on('-f', '--first_name FIRSTNAME', 'The first name') { |o| options[:first_name] = o } 
    opt.on('-l', '--last_name LASTNAME', 'The last name') { |o| options[:last_name] = o } 
end.parse! 

e:

$ ruby test.rb -h 
Usage: test [options] 
    -f, --first_name FIRSTNAME  The first name 
    -l, --last_name LASTNAME   The last name 

OptionParser supporta anche la conversione del parametro a un tipo, ad esempio un numero intero o un array. Fare riferimento alla documentazione per ulteriori esempi e informazioni.

Si dovrebbe anche guardare l'elenco di questioni connesse al diritto:

0

ho personnaly uso Docopt. Questo è molto più chiaro, manutenibile e di lettura.

Dai un'occhiata a online Ruby implementation doc per esempi. L'utilizzo è davvero semplice. Codice

gem install docopt 

Rubino:

doc = <<DOCOPT 
My program who says hello 

Usage: 
    #{__FILE__} --first_name=<first_name> --last_name=<last_name> 
DOCOPT 

begin 
    args = Docopt::docopt(doc) 
rescue Docopt::Exit => e 
    puts e.message 
    exit 
end 

print "Hello #{args['--first_name']} #{args['--last_name']}" 

quindi chiamando:

$ ./says_hello.rb --first_name=Homer --last_name=Simpsons 
Hello Homer Simpsons 

E senza argomenti:

$ ./says_hello.rb 
Usage: 
    says_hello.rb --first_name=<first_name> --last_name=<last_name> 
Problemi correlati