2010-03-30 23 views
17

In OptionParser posso rendere obbligatoria un'opzione, ma se escludo quel valore prenderà il nome di qualsiasi opzione successiva come valore, rovinando il resto dell'analisi della riga di comando. Ecco un banco di prova che riecheggia i valori delle opzioni:Come gestisco un argomento obbligatorio mancante in Ruby OptionParser?

$ ./test_case.rb --input foo --output bar 
output bar 
input foo 

Ora lasciare fuori il valore per la prima opzione:

$ ./test_case.rb --input --output bar 
input --output 

C'è qualche modo per impedire che prendere un altro nome opzione come un valore? Grazie!

Ecco il codice di test case:

#!/usr/bin/env ruby 
require 'optparse' 
files = Hash.new 

option_parser = OptionParser.new do |opts| 
    opts.on('-i', '--input FILENAME', 'Input filename - required') do |filename| 
    files[:input] = filename 
    end 
    opts.on('-o', '--output FILENAME', 'Output filename - required') do |filename| 
    files[:output] = filename 
    end 
end 

begin 
    option_parser.parse!(ARGV) 
rescue OptionParser::ParseError 
    $stderr.print "Error: " + $! + "\n" 
    exit 
end 

files.keys.each do |key| 
    print "#{key} #{files[key]}\n" 
end 

risposta

2

In questo caso, l'opzione obbligatoria --output manca, in modo da fare questo dopo aver chiamato parse!:

unless files[:input] && files[:output] 
    $stderr.puts "Error: you must specify both --input and --output options." 
    exit 1 
end 
+0

Già ... che risolve il caso di test, ma sono davvero sperando che possa fare questo all'interno del blocco OptionParser.new in cui essa appartiene. Si suppone che sia possibile includere un/pattern/in una chiamata on() che qualsiasi argomento deve corrispondere - ma non sto ancora funzionando. Grazie! –

1

OK - questo funziona - i regolari espressione nella chiamata on() consente qualsiasi stringa finché non inizia con un '-'

Se non si passa un argomento a --input e th Prima è un'altra opzione quindi prenderà quella chiave di opzione come argomento di --input. (ad es. --input --output). Il regexp lo rileva e quindi controllo il messaggio di errore. Se l'argomento che riporta inizia con "-", restituisco il messaggio di errore corretto, vale a dire che c'è un argomento mancante. Non carino ma sembra funzionare.

Ecco il mio banco di prova di lavoro:

#!/usr/bin/env ruby 
require 'optparse' 
files = Hash.new 

option_parser = OptionParser.new do |opts| 
    opts.on('-i FILENAME', '--input FILENAME', /\A[^\-]+/, 'Input filename - required') do |filename| 
    files[:input] = filename 
    end 
    opts.on('-o FILENAME', '--output FILENAME', /\A[^\-]+/, 'Output filename - required') do |filename| 
    files[:output] = filename 
    end 
end 

begin 
    option_parser.parse!(ARGV) 
rescue OptionParser::ParseError 
    if $!.to_s =~ /invalid\s+argument\:\s+(\-\-\S+)\s+\-/ 
    $stderr.print "Error: missing argument: #{$1}\n" 
    else 
    $stderr.print "Error: " + $! + "\n" 
    end 
    exit 
end 

files.keys.each do |key| 
    print "#{key} #{files[key]}\n" 
end 
9

Che cosa si vuole fare non è una buona idea. Cosa succede se hai davvero un file chiamato "--output"? Questo è un nome di file perfettamente valido su Unix. L'analisi delle opzioni di ogni programma Unix funziona come fa il rubino, quindi non dovresti cambiarlo, perché il tuo programma sarà arbitrariamente diverso da tutto il resto, il che è fonte di confusione e viola il "principio di minima sorpresa".

La vera domanda è: perché stai avendo questo problema in primo luogo? Forse stai facendo girare il tuo programma da un altro programma, e il programma genitore fornisce un nome file vuoto come parametro a --input, che gli permette di vedere --output come parametro su --input. È possibile aggirare il citando sempre il nome di file si passa sulla linea di comando:

./test_case.rb --input "" --output "bar" 

Poi --input sarà vuota, e che è facile da rilevare.

Si noti inoltre che se --input è impostato su --output (e --output non è un file reale), è sufficiente provare ad aprire il file --input. Se fallisce, stampare un messaggio del tipo:

can't open input file: --output: file not found 

E questo dovrebbe far capire all'utente che cosa hanno fatto di sbagliato.

+3

+1 non provare mai a risolvere qualcosa nascondendolo con codice intelligente –

2

provare questo:

opts.on('-i', '--input FILENAME', 'Input filename - required') do |filename| 
    files[:input] = filename 
end 

opts.on('-o', '--output FILENAME', 'Output filename - required') do |filename| 
    files[:output] = filename 
end 

opts.on("-h", "--help", "Show this message") do 
    puts opts 
    exit 
end 


begin 
    ARGV << "-h" if ARGV.size != 2 
    option_parser.parse!(ARGV) 
rescue OptionParser::ParseError 
    $stderr.print "Error: " + $! + "\n" 
    exit 
end 
Problemi correlati