J'écris un script et je veux exiger un --Host
changer avec la valeur, mais si le --Host
le commutateur n'est pas spécifié, je veux que l'analyse de l'option échoue.
Je n'arrive pas à comprendre comment faire ça. Les documents semblent spécifier uniquement comment rendre la valeur d'argument obligatoire, pas le commutateur lui-même.
Je suppose que vous utilisez optparse ici, bien que la même technique fonctionne pour d'autres bibliothèques d'analyse d'options.
La méthode la plus simple consiste probablement à analyser les paramètres à l'aide de la bibliothèque d'analyse d'options choisie, puis à lever une exception OptionParser :: MissingArgument si la valeur de Host est nulle.
Le code suivant illustre
#!/usr/bin/env Ruby
require 'optparse'
options = {}
optparse = OptionParser.new do |opts|
opts.on('-h', '--Host HOSTNAME', "Mandatory Host Name") do |f|
options[:Host] = f
end
end
optparse.parse!
#Now raise an exception if we have not found a Host option
raise OptionParser::MissingArgument if options[:Host].nil?
puts "Host = #{options[:Host]}"
Exécution de cet exemple avec une ligne de commande de
./program -h somehost
affiche simplement "Host = somehost"
Lors de l'exécution avec un -h manquant et aucun nom de fichier ne produit la sortie suivante
./program:15: missing argument: (OptionParser::MissingArgument)
Et courir avec une ligne de commande de ./program -h produit
/usr/lib/Ruby/1.8/optparse.rb:451:in `parse': missing argument: -h (OptionParser::MissingArgument)
from /usr/lib/Ruby/1.8/optparse.rb:1288:in `parse_in_order'
from /usr/lib/Ruby/1.8/optparse.rb:1247:in `catch'
from /usr/lib/Ruby/1.8/optparse.rb:1247:in `parse_in_order'
from /usr/lib/Ruby/1.8/optparse.rb:1241:in `order!'
from /usr/lib/Ruby/1.8/optparse.rb:1332:in `permute!'
from /usr/lib/Ruby/1.8/optparse.rb:1353:in `parse!'
from ./program:13
Une approche utilisant optparse qui fournit une sortie conviviale sur les commutateurs manquants:
#!/usr/bin/env Ruby
require 'optparse'
options = {}
optparse = OptionParser.new do |opts|
opts.on('-f', '--from SENDER', 'username of sender') do |sender|
options[:from] = sender
end
opts.on('-t', '--to RECIPIENTS', 'comma separated list of recipients') do |recipients|
options[:to] = recipients
end
options[:number_of_files] = 1
opts.on('-n', '--num_files NUMBER', Integer, "number of files to send (default #{options[:number_of_files]})") do |number_of_files|
options[:number_of_files] = number_of_files
end
opts.on('-h', '--help', 'Display this screen') do
puts opts
exit
end
end
begin
optparse.parse!
mandatory = [:from, :to] # Enforce the presence of
missing = mandatory.select{ |param| options[param].nil? } # the -t and -f switches
unless missing.empty? #
raise OptionParser::MissingArgument.new(missing.join(', ')) #
end #
rescue OptionParser::InvalidOption, OptionParser::MissingArgument #
puts $!.to_s # Friendly output when parsing fails
puts optparse #
exit #
end #
puts "Performing task with options: #{options.inspect}"
Courir sans le -t
ou -f
commutateurs affiche la sortie suivante:
Missing options: from, to
Usage: test_script [options]
-f, --from SENDER username of sender
-t, --to RECIPIENTS comma separated list of recipients
-n, --num_files NUMBER number of files to send (default 1)
-h, --help
L'exécution de la méthode d'analyse dans une clause begin/rescue permet un formatage convivial sur d'autres échecs tels que des arguments manquants ou des valeurs de commutateur non valides, par exemple, essayez de passer une chaîne pour le -n
commutateur.
J'ai transformé cela en un joyau que vous pouvez télécharger et installer à partir de rubygems.org:
gem install pickled_optparse
Et vous pouvez extraire le code source du projet mis à jour sur github:
http://github.com/PicklePumpers/pickled_optparse
- Info post plus ancienne -
C'était vraiment, vraiment un bug pour moi donc je l'ai corrigé et gardé l'utilisation super SEC.
Pour rendre un commutateur requis, ajoutez simplement un symbole: required n'importe où dans le tableau d'options comme ceci:
opts.on("-f", "--foo [Bar]", String, :required, "Some required option") do |option|
@options[:foo] = option
end
Ensuite, à la fin de votre bloc OptionParser, ajoutez l'un d'eux pour imprimer les commutateurs manquants et les instructions d'utilisation:
if opts.missing_switches?
puts opts.missing_switches
puts opts
exit
end
Et enfin, pour que tout fonctionne, vous devez ajouter le fichier "optparse_required_switches.rb" suivant à votre projet quelque part et l'exiger lorsque vous effectuez votre analyse de ligne de commande.
J'ai écrit un petit article avec un exemple sur mon blog: http://picklepumpers.com/wordpress/?p=949
Et voici le fichier OptionParser modifié avec un exemple de son utilisation:
required_switches_example.rb
#!/usr/bin/env Ruby
require 'optparse'
require_relative 'optparse_required_switches'
# Configure options based on command line options
@options = {}
OptionParser.new do |opts|
opts.banner = "Usage: test [options] in_file[.srt] out_file[.srt]"
# Note that :required can be anywhere in the parameters
# Also note that OptionParser is bugged and will only check
# for required parameters on the last option, not my bug.
# required switch, required parameter
opts.on("-s Short", String, :required, "a required switch with just a short") do |operation|
@options[:operation] = operation
end
# required switch, optional parameter
opts.on(:required, "--long [Long]", String, "a required switch with just a long") do |operation|
@options[:operation] = operation
end
# required switch, required parameter
opts.on("-b", "--both ShortAndLong", String, "a required switch with short and long", :required) do |operation|
@options[:operation] = operation
end
# optional switch, optional parameter
opts.on("-o", "--optional [Whatever]", String, "an optional switch with short and long") do |operation|
@options[:operation] = operation
end
# Now we can see if there are any missing required
# switches so we can alert the user to what they
# missed and how to use the program properly.
if opts.missing_switches?
puts opts.missing_switches
puts opts
exit
end
end.parse!
optparse_required_switches.rb
# Add required switches to OptionParser
class OptionParser
# An array of messages describing the missing required switches
attr_reader :missing_switches
# Convenience method to test if we're missing any required switches
def missing_switches?
!@missing_switches.nil?
end
def make_switch(opts, block = nil)
short, long, nolong, style, pattern, conv, not_pattern, not_conv, not_style = [], [], []
ldesc, sdesc, desc, arg = [], [], []
default_style = Switch::NoArgument
default_pattern = nil
klass = nil
n, q, a = nil
# Check for required switches
required = opts.delete(:required)
opts.each do |o|
# argument class
next if search(:atype, o) do |pat, c|
klass = notwice(o, klass, 'type')
if not_style and not_style != Switch::NoArgument
not_pattern, not_conv = pat, c
else
default_pattern, conv = pat, c
end
end
# directly specified pattern(any object possible to match)
if (!(String === o || Symbol === o)) and o.respond_to?(:match)
pattern = notwice(o, pattern, 'pattern')
if pattern.respond_to?(:convert)
conv = pattern.method(:convert).to_proc
else
conv = SPLAT_PROC
end
next
end
# anything others
case o
when Proc, Method
block = notwice(o, block, 'block')
when Array, Hash
case pattern
when CompletingHash
when nil
pattern = CompletingHash.new
conv = pattern.method(:convert).to_proc if pattern.respond_to?(:convert)
else
raise ArgumentError, "argument pattern given twice"
end
o.each {|pat, *v| pattern[pat] = v.fetch(0) {pat}}
when Module
raise ArgumentError, "unsupported argument type: #{o}", ParseError.filter_backtrace(caller(4))
when *ArgumentStyle.keys
style = notwice(ArgumentStyle[o], style, 'style')
when /^--no-([^\[\]=\s]*)(.+)?/
q, a = $1, $2
o = notwice(a ? Object : TrueClass, klass, 'type')
not_pattern, not_conv = search(:atype, o) unless not_style
not_style = (not_style || default_style).guess(arg = a) if a
default_style = Switch::NoArgument
default_pattern, conv = search(:atype, FalseClass) unless default_pattern
ldesc << "--no-#{q}"
long << 'no-' + (q = q.downcase)
nolong << q
when /^--\[no-\]([^\[\]=\s]*)(.+)?/
q, a = $1, $2
o = notwice(a ? Object : TrueClass, klass, 'type')
if a
default_style = default_style.guess(arg = a)
default_pattern, conv = search(:atype, o) unless default_pattern
end
ldesc << "--[no-]#{q}"
long << (o = q.downcase)
not_pattern, not_conv = search(:atype, FalseClass) unless not_style
not_style = Switch::NoArgument
nolong << 'no-' + o
when /^--([^\[\]=\s]*)(.+)?/
q, a = $1, $2
if a
o = notwice(NilClass, klass, 'type')
default_style = default_style.guess(arg = a)
default_pattern, conv = search(:atype, o) unless default_pattern
end
ldesc << "--#{q}"
long << (o = q.downcase)
when /^-(\[\^?\]?(?:[^\\\]]|\\.)*\])(.+)?/
q, a = $1, $2
o = notwice(Object, klass, 'type')
if a
default_style = default_style.guess(arg = a)
default_pattern, conv = search(:atype, o) unless default_pattern
end
sdesc << "-#{q}"
short << Regexp.new(q)
when /^-(.)(.+)?/
q, a = $1, $2
if a
o = notwice(NilClass, klass, 'type')
default_style = default_style.guess(arg = a)
default_pattern, conv = search(:atype, o) unless default_pattern
end
sdesc << "-#{q}"
short << q
when /^=/
style = notwice(default_style.guess(arg = o), style, 'style')
default_pattern, conv = search(:atype, Object) unless default_pattern
else
desc.Push(o)
end
end
default_pattern, conv = search(:atype, default_style.pattern) unless default_pattern
if !(short.empty? and long.empty?)
s = (style || default_style).new(pattern || default_pattern, conv, sdesc, ldesc, arg, desc, block)
elsif !block
if style or pattern
raise ArgumentError, "no switch given", ParseError.filter_backtrace(caller)
end
s = desc
else
short << pattern
s = (style || default_style).new(pattern, conv, nil, nil, arg, desc, block)
end
# Make sure required switches are given
if required && !(default_argv.include?("-#{short[0]}") || default_argv.include?("--#{long[0]}"))
@missing_switches ||= [] # Should be placed in initialize if incorporated into Ruby proper
# This is more clear but ugly and long.
#missing = "-#{short[0]}" if !short.empty?
#missing = "#{missing} or " if !short.empty? && !long.empty?
#missing = "#{missing}--#{long[0]}" if !long.empty?
# This is less clear and uglier but shorter.
missing = "#{"-#{short[0]}" if !short.empty?}#{" or " if !short.empty? && !long.empty?}#{"--#{long[0]}" if !long.empty?}"
@missing_switches << "Missing switch: #{missing}"
end
return s, short, long,
(not_style.new(not_pattern, not_conv, sdesc, ldesc, nil, desc, block) if not_style),
nolong
end
end
J'ai trouvé une solution claire et concise qui résume vos contributions. Il soulève un OptionParser::MissingArgument
exception avec les arguments manquants comme message. Cette exception est interceptée dans le bloc rescue
avec le reste des exceptions provenant de OptionParser
.
#!/usr/bin/env Ruby
require 'optparse'
options = {}
optparse = OptionParser.new do |opts|
opts.on('-h', '--Host hostname', "Host name") do |Host|
options[:Host] = Host
end
end
begin
optparse.parse!
mandatory = [:Host]
missing = mandatory.select{ |param| options[param].nil? }
raise OptionParser::MissingArgument, missing.join(', ') unless missing.empty?
rescue OptionParser::ParseError => e
puts e
puts optparse
exit
end
Exécuter cet exemple:
./program
missing argument: Host
Usage: program [options]
-h, --Host hostname Host name
Si Host est requis, alors ce n'est sûrement pas une option , c'est un argument .
Dans cet esprit, voici un moyen de résoudre votre problème. Vous pouvez interroger le tableau ARGV
pour voir si un hôte a été spécifié et, s'il ne l'a pas été, puis appeler abort("You must specify a Host!")
, ou similaire, pour faire quitter votre programme avec une erreur statut.
Si vous faites quelque chose comme ça:
opts.on('-h', '--Host',
'required Host name [STRING]') do |h|
someoptions[:Host] = h || nil
end
Puis le someoptions[:Host]
sera soit la valeur de la ligne de commande ou nil
(si vous ne fournissez pas --Host et/ou aucune valeur après --Host) et vous pouvez la tester facilement (et échouer conditionnellement) après l'analyse:
fail "Hostname not provided" unless someoptions[:Host]
L'idée est de définir un OptionParser
, puis parse!
it, et puts
it si certains champs sont manquants. Définir filename
sur une chaîne vide par défaut n'est probablement pas la meilleure solution, mais vous avez eu l'idée.
require 'optparse'
filename = ''
options = OptionParser.new do |opts|
opts.banner = "Usage: Swift-code-style.rb [options]"
opts.on("-iNAME", "--input-filename=NAME", "Input filename") do |name|
filename = name
end
opts.on("-h", "--help", "Prints this help") do
puts opts
exit
end
end
options.parse!
if filename == ''
puts "Missing filename.\n---\n"
puts options
exit
end
puts "Processing '#{filename}'..."
Si -i filename
est manquant, il affiche:
~/prj/gem/Swift-code-kit ./Swift-code-style.rb
Missing filename.
---
Usage: Swift-code-style.rb [options]
-i, --input-filename=NAME Input filename
-h, --help Prints this help
La réponse de unknown (google) est bonne, mais contient une erreur mineure.
rescue OptionParser::InvalidArgument, OptionParser::MissingArgument
devrait être
OptionParser::InvalidOption, OptionParser::MissingArgument
Autrement, optparse.parse!
déclenchera la sortie d'erreur standard pour OptionParser::InvalidOption
, pas le message personnalisé.