Analyse d'options de ligne de commande vraiment bon marché dans Ruby

114

EDIT: S'il vous plaît, s'il vous plaît , veuillez lire les deux exigences énumérées au bas de cet article avant de répondre. Les gens continuent d'afficher leurs nouveaux joyaux et bibliothèques, etc., qui ne répondent clairement pas aux exigences.

Parfois, je veux pirater à peu de frais certaines options de ligne de commande dans un simple script. Une façon amusante de le faire, sans avoir à gérer les getopts ou l'analyse ou quoi que ce soit du genre, est:

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

Ce n'est pas tout à fait la syntaxe normale des options Unix, car elle acceptera les paramètres de ligne de commande d'options sans option, comme dans " myprog -i foo bar -q", mais je peux vivre avec cela. (Certaines personnes, comme les développeurs Subversion, préfèrent cela. Parfois moi aussi.)

Une option qui est simplement présente ou absente ne peut pas être implémentée beaucoup plus simplement que ce qui précède. (Une affectation, un appel de fonction, un effet secondaire.) Existe-t-il un moyen tout aussi simple de traiter les options qui prennent un paramètre, tel que " -f filename "?

ÉDITER:

Un point que je n’ai pas souligné plus tôt, car il ne m’était pas devenu clair jusqu’à ce que l’auteur de Trollop ait mentionné que la bibliothèque tenait «dans un fichier [800 lignes]», c’est que je ne cherche pas seulement à nettoyer syntaxe, mais pour une technique qui présente les caractéristiques suivantes:

  1. L'intégralité du code peut être inclus dans le fichier de script (sans surcharger le script lui-même, qui peut ne contenir que quelques dizaines de lignes), de sorte que l'on puisse déposer un seul fichier dans un répertoire binsur n'importe quel système avec un Ruby 1.8 standard . [5-7] installation et utilisez-le. Si vous ne pouvez pas écrire un script Ruby qui n'a pas d'instructions require et où le code pour analyser quelques options se trouve sous une douzaine de lignes environ, vous échouez à cette exigence.

  2. Le code est suffisamment petit et simple pour qu'on s'en souvienne suffisamment pour taper directement du code qui fera l'affaire, plutôt que de couper et coller ailleurs. Pensez à la situation où vous êtes sur la console d'un serveur pare-feu sans accès à Internet et que vous souhaitez lancer un script rapide à utiliser par un client. Je ne sais pas pour vous, mais (en plus de ne pas répondre à l'exigence ci-dessus), mémoriser même les 45 lignes de micro-optparse simplifié n'est pas quelque chose que je veux faire.

cjs
la source
2
Vous êtes curieux de savoir l'objection contre getoptlong?
Mark Carey
La verbosité de celui-ci. Avec getoptlog, le code d'analyse des options est parfois plus long que la partie du script qui fait réellement le travail. Ce n'est pas seulement un problème esthétique, mais un problème de coût de maintenance.
cjs
8
Je ne comprends pas l'exigence d'inclusion de script - les deux getoptlonget optparsesont dans la bibliothèque standard de ruby, vous n'avez donc pas besoin de les copier lors du déploiement de votre script - si ruby ​​fonctionne sur cette machine, alors require 'optparse'ou require 'getoptlong'fonctionnera aussi.
rampion
Voir stackoverflow.com/questions/21357953/… , ainsi que la réponse de William Morgan ci-dessous à propos de Trollop.
peurless_fool
@CurtSampson Je n'arrive pas à croire combien de personnes n'ont pas répondu à votre question. Quoi qu'il en soit, j'ai finalement obtenu une bonne réponse à propos de 3 messages vers le bas XD XD
OneChillDude

Réponses:

235

En tant qu'auteur de Trollop , je ne peux pas croire ce que les gens pensent raisonnable dans un analyseur d'options. Sérieusement. C'est stupéfiant.

Pourquoi devrais-je avoir à créer un module qui étend un autre module pour analyser les options? Pourquoi devrais-je devoir sous-classer quoi que ce soit? Pourquoi devrais-je devoir m'abonner à un "framework" juste pour analyser la ligne de commande?

Voici la version Trollop de ce qui précède:

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

Et c'est tout. optsest maintenant un hachage avec les touches :quiet, :interactiveet:filename . Vous pouvez en faire ce que vous voulez. Et vous obtenez une belle page d'aide, formatée pour s'adapter à la largeur de votre écran, des noms d'arguments courts automatiques, une vérification de type ... tout ce dont vous avez besoin.

C'est un fichier, vous pouvez donc le déposer dans votre répertoire lib / si vous ne voulez pas de dépendance formelle. Il a un DSL minimal qui est facile à prendre en main.

LOC par personne en option. Cela compte.

Freedom_Ben
la source
39
BTW, +1 pour avoir écrit Trollop (qui avait déjà été mentionné ici), mais n'hésitez pas à atténuer un peu le premier paragraphe.
cjs
33
Il a le droit de se plaindre dans ce cas, j'en ai peur. Quand vous regardez les alternatives: [1 ] [2 ] [3 ], pour ce qui est fondamentalement juste le traitement d'un simple tableau de chaînes (non vraiment, laissez cela pénétrer), vous ne pouvez pas vous empêcher de vous demander POURQUOI? Que gagnez-vous de tout ce ballonnement? Ce n'est pas C, où les chaînes sont "problématiques". Bien sûr à chacun le sien. :)
srcspider
50
Veuillez ne pas atténuer cela un peu. C'est une chape juste, frère.
William Pietri
7
N'hésitez pas à atténuer un peu le dixième mot.
Andrew Grimm
3
+1 pour Trollop. Je l'utilise pour mon système d'automatisation de test et cela fonctionne. De plus, il est si facile de coder avec que parfois je réorganise ma bannière juste pour en ressentir la joie.
kinofrost
76

Je partage votre dégoût pour require 'getopts', principalement en raison de la génialité qui est 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}
rampion
la source
6
Merci, je ne vois pas en quoi cela ne correspond pas à la demande des OP, en particulier compte tenu de son tout dans la bibliothèque standard, par rapport à la nécessité d'installer / de vendre un code non standard
dolzenko
3
cela ressemble à la version trollop sauf qu'il n'a pas besoin du fichier supplémentaire.
Claudiu
59

Voici la technique standard que j'utilise habituellement:

#!/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}")
cjs
la source
3
Répond à la question, mais mec, Trollop semble être beaucoup plus facile à gérer. Pourquoi réinventer la roue alors que la roue préfabriquée est tellement plus douce?
Mikey TK
7
La roue préfabriquée n'est pas plus lisse. Relisez attentivement la question, en prêtant une attention particulière aux exigences.
cjs
2
+1 Parfois, vous devez réinventer la roue, parce que vous ne voulez pas ou ne pouvez tout simplement pas utiliser d'autres dépendances comme Trollop.
lzap le
Trollop n'a pas besoin d'être installé comme un bijou. Vous pouvez simplement déposer un fichier dans votre libdossier ou code et l'utiliser sans même toucher rubygems.
Overbryd
Pour moi, je devais passer when /^-/ then usage("Unknown option: #{ARGV[0].inspect}")à when /^-/ then usage("Unknown option: #{ARGV.shift.inspect}")ou cela entrerait dans une boucle d'utilisation infinie
casey
36

Puisque personne ne semble le mentionner et que le titre fait référence à une analyse de ligne de commande bon marché , pourquoi ne pas laisser l'interpréteur Ruby faire le travail à votre place? Si vous passez le -scommutateur (dans votre shebang, par exemple), vous obtenez gratuitement des commutateurs simples, affectés à des variables globales à une seule lettre. Voici votre exemple d'utilisation de ce commutateur:

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

Et voici le résultat lorsque je l'enregistre sous ./testet le 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"]

Voir ruby -hpour plus de détails.

Cela doit être aussi bon marché que possible. Cela lèvera une NameError si vous essayez un commutateur comme -:, donc il y a une validation ici. Bien sûr, vous ne pouvez pas avoir de commutateurs après un argument non-commutateur, mais si vous avez besoin de quelque chose de sophistiqué, vous devriez vraiment utiliser au minimum OptionParser. En fait, la seule chose qui m'ennuie à propos de cette technique est que vous recevrez un avertissement (si vous les avez activées) lors de l'accès à une variable globale non définie, mais c'est toujours faux, donc cela fonctionne très bien pour les outils jetables et rapide scripts.

Une mise en garde signalée par FelipeC dans les commentaires de " Comment faire une analyse des options de ligne de commande vraiment bon marché dans Ruby ", est que votre shell pourrait ne pas prendre en charge le shebang à 3 jetons; vous devrez peut-être remplacer /usr/bin/env ruby -wpar le chemin réel de votre ruby ​​(comme /usr/local/bin/ruby -w), ou l'exécuter à partir d'un script wrapper, ou quelque chose.

bjjb
la source
2
Merci :) J'espère bien qu'il n'a pas attendu cette réponse depuis deux ans.
DarkHeart
3
J'attends en effet cette réponse depuis deux ans. :-) Plus sérieusement, c'est le genre de réflexion intelligente que je recherchais. L'avertissement est un peu ennuyeux, mais je peux penser à des moyens d'atténuer cela.
cjs
Heureux de pouvoir (éventuellement) aider, @CurtSampson, les drapeaux de l'IRM sont extraits directement de Perl, où ils ont tendance à être utilisés gratuitement dans les one-liners shell. N'hésitez pas à accepter, si la réponse vous est toujours utile. :)
bjjb
1
Vous ne pouvez pas utiliser plusieurs arguments dans un shebang sous Linux. / usr / bin / env: 'ruby -s': aucun fichier ou répertoire de ce type
FelipeC
13

J'ai construit micro-optparse pour répondre à ce besoin évident d'un analyseur d'options court mais facile à utiliser. Il a une syntaxe similaire à Trollop et fait 70 lignes. Si vous n'avez pas besoin de validations et que vous pouvez vous passer de lignes vides, vous pouvez le réduire à 45 lignes. Je pense que c'est exactement ce que vous recherchiez.

Exemple court:

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!

Appel du script avec -hou --helpimprimera

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

Il vérifie si l'entrée est du même type que la valeur par défaut, génère des accesseurs courts et longs, imprime des messages d'erreur descriptifs si des arguments non valides sont donnés et plus encore.

J'ai comparé plusieurs analyseurs d' options en utilisant chaque analyseur d'options pour le problème que j'ai eu. Vous pouvez utiliser ces exemples et mon résumé pour prendre une décision informative. N'hésitez pas à ajouter plus d'implémentations à la liste. :)

Florian Pilz
la source
La bibliothèque elle-même a l'air d'être géniale. Cependant, n'est-il pas malhonnête de comparer le nombre de lignes avec Trollop puisque vous dépendez et avez besoin de optparse(donner ou prendre) 1937 lignes.
Télémaque
6
Comparer le nombre de lignes est tout à fait acceptable, car il optparses'agit d'une bibliothèque par défaut, c'est-à-dire qu'elle est livrée avec chaque installation de ruby. Trollopest une bibliothèque tierce, vous devez donc importer le code complet chaque fois que vous souhaitez l'inclure dans un projet. µ-optparse ne nécessite toujours que les ~ 70 lignes, car il optparseest déjà là.
Florian Pilz
8

Je comprends tout à fait pourquoi vous voulez éviter optparse - il peut en avoir trop. Mais il y a quelques solutions beaucoup plus "légères" (comparées à OptParse) qui viennent sous forme de bibliothèques, mais qui sont assez simples pour qu'une seule installation gem en vaille la peine.

Par exemple, consultez cet exemple d'OptiFlag . Juste quelques lignes pour le traitement. Un exemple légèrement tronqué adapté à votre cas:

require 'optiflag'

module Whatever extend OptiFlagSet
  flag "f"
  and_process!
end 

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

Il existe également des tonnes d'exemples personnalisés . Je me souviens en avoir utilisé un autre qui était encore plus facile, mais cela m'a échappé pour l'instant mais je reviendrai et ajouterai un commentaire ici si je le trouve.

Peter Cooper
la source
N'hésitez pas à modifier votre réponse pour mieux répondre à la question clarifiée.
cjs
4

C'est ce que j'utilise pour des arguments vraiment, vraiment bon marché:

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

main

donc si vous exécutez, programname foo baril appelle foo puis bar. C'est pratique pour les scripts jetables.

chrismealy
la source
3

Vous pouvez essayer quelque chose comme:

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

Avez-vous considéré Thor par des wycats? Je pense que c'est beaucoup plus propre qu'optparse. Si vous avez déjà écrit un script, il pourrait être un peu plus de travail de le formater ou de le refactoriser pour Thor, mais cela rend les options de gestion très simples.

Voici l'exemple d'extrait du 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 mappe automatiquement les commandes comme telles:

app install myname --force

Cela est converti en:

MyApp.new.install("myname")
# with {'force' => true} as options hash
  1. Hériter de Thor pour transformer une classe en mappeur d'options
  2. Mappez des identificateurs non valides supplémentaires à des méthodes spécifiques. Dans ce cas, convertissez -L en: liste
  3. Décrivez la méthode ci-dessous. Le premier paramètre est les informations d'utilisation et le deuxième paramètre est la description.
  4. Fournissez des options supplémentaires. Ceux-ci seront rassemblés à partir des paramètres et des paramètres. Dans ce cas, une option --force et une option -f sont ajoutées.
Jack Chu
la source
J'aime le mappage de commandes, car un seul binaire avec un tas de sous-commandes est quelque chose que je fais souvent. Pourtant, même si vous vous êtes éloigné de la «lumière». Pourriez-vous trouver un moyen encore plus simple d'exprimer cette même fonctionnalité? Et si vous n'aviez pas besoin d'imprimer la --helpsortie? Et si "head myprogram.rb" était la sortie d'aide?
cjs
3

Voici mon analyseur d'options rapide et sale préféré:

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

Les options sont des expressions régulières, donc "-h" correspondra également à "--help".

Lisible, facile à retenir, pas de bibliothèque externe et code minimal.

EdwardTeach
la source
Oui ça le ferait. Si c'est un problème, vous pouvez ajouter plus de regex, par exemple/-h(\b|elp)
EdwardTeach
2

Trollop est assez bon marché.

g33kz0r
la source
Ce serait, < trollop.rubyforge.org >. Je l'aime plutôt, je pense, même si je ne cherchais vraiment pas une bibliothèque.
cjs
C'est vrai, c'est une bibliothèque. Cependant, à <800 LOC, c'est assez négligeable, en plus. gitorious.org/trollop/mainline/blobs/master/lib/trollop.rb
g33kz0r
1
Je pensais un peu que peut-être 30 à 50 lignes seraient bien, si j'allais jusqu'à utiliser une «bibliothèque». Mais là encore, je suppose qu'une fois que vous avez un fichier séparé rempli de code, la conception de l'API est plus importante que le nombre de lignes. Pourtant, je ne suis pas sûr de vouloir l'inclure dans un script ponctuel que je veux simplement placer dans le répertoire bin sur un système aléatoire.
cjs
1
Vous avez les choses à l'envers: il s'agit d'éviter d'avoir à avoir une stratégie de déploiement plus complexe.
cjs
1
Vous avez tout à fait tort, parce que vous interprétez totalement (ou ignorez) les besoins et les intentions de la personne qui a posé la question. Je vous suggère de relire attentivement la question, en particulier les deux derniers points.
cjs
2

Si vous voulez un analyseur de ligne de commande simple pour les commandes clé / valeur sans l'utilisation de gemmes:

Mais cela ne fonctionne que si vous avez toujours des paires clé / valeur.

# 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']}"

Si vous n'avez pas besoin de vérification, vous pouvez simplement utiliser:

opts = {} 

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

Voici l'extrait de code que j'utilise en haut de la plupart de mes scripts:

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) }
}

Je déteste aussi avoir besoin de fichiers supplémentaires dans mes scripts rapides et sales. Ma solution est très proche de ce que vous demandez. Je colle un extrait de code de 10 lignes en haut de l'un de mes scripts qui analyse la ligne de commande et colle les arguments positionnels et bascule dans un objet Hash (généralement attribué à un objet que j'ai nommé arghash dans les exemples ci-dessous).

Voici un exemple de ligne de commande que vous voudrez peut-être analyser ...

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

Ce qui deviendrait un Hash comme celui-ci.

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

En plus de cela, deux méthodes pratiques sont ajoutées au hachage:

  • argc() renverra le nombre d'arguments non-switch.
  • switches() renverra un tableau contenant les clés des commutateurs présents

C'est moyen pour permettre des trucs rapides et sales comme ...

  • Validez que j'ai le bon nombre d'arguments de position quels que soient les commutateurs passés ( arghash.argc == 2 )
  • Accédez aux arguments de position par leur position relative, indépendamment des commutateurs apparaissant avant ou entrecoupés d'arguments de position (par exemple, arghash[1]obtient toujours le deuxième argument non-commutateur).
  • Prise en charge des commutateurs attribués à une valeur dans la ligne de commande, tels que "--max = 15", auxquels il est possible d'accéder et arghash['--max=']qui donne une valeur de '15' à partir de l'exemple de ligne de commande.
  • Testez la présence ou l'absence d'un commutateur dans la ligne de commande en utilisant une notation très simple telle que arghash['-s']qui prend la valeur true s'il est présent et nul s'il est absent.
  • Testez la présence d'un interrupteur ou d'alternatives d'interrupteurs à l'aide d'opérations de réglage telles que

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

  • Identifiez l'utilisation de commutateurs non valides à l'aide d'opérations définies telles que

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

  • Spécifiez les valeurs par défaut pour les arguments manquants à l'aide d'un simple Hash.merge()comme l'exemple ci-dessous qui remplit une valeur pour -max = si l'un n'a pas été défini et ajoute un quatrième argument de position s'il n'a pas été transmis.

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

David Foster
la source
(J'ai modifié ceci pour nettoyer et améliorer le formatage du code, principalement en utilisant l'alignement pour rendre le bloc et la structure de contrôle plus clair, ce qui me semble particulièrement important dans quelque chose d'aussi dense avec la ponctuation. Mais si vous détestez le nouveau formatage, n'hésitez pas pour annuler la modification.)
cjs
C'est plutôt agréable, sinon la chose la plus facile au monde à lire. J'aime le fait que cela démontre également que changer la "syntaxe" des arguments (ici, autoriser les options après les arguments de position et interdire les arguments d'option sauf en utilisant =) peut faire une différence dans le code dont vous avez besoin.
cjs
Merci pour le reformatage. C'est vraiment obscur à lire et on pourrait facilement échanger la longueur du code pour plus de clarté. Maintenant que je fais confiance à ce code, plus ou moins, je le traite comme un bijou et je n'essaye jamais de comprendre ce qu'il fait sous les couvertures (donc la clarté n'est plus importante maintenant que j'ai confiance).
David Foster le
1

Ceci est très similaire à la réponse acceptée, mais ARGV.delete_ifc'est ce que j'utilise dans mon analyseur simple . La seule vraie différence est que les options avec des arguments doivent être ensemble (par exemple -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}"
FelipeC
la source
0

Apparemment, @WilliamMorgan et moi pensons pareillement. Je viens de publier hier soir sur Github ce que je vois maintenant est une bibliothèque similaire à Trollop (nommée comment?) Après avoir fait une recherche d'OptionParser sur Github, voir Switches

Il y a quelques différences, mais la philosophie est la même. Une différence évidente est que Switches dépend d'OptionParser.

thoran
la source
0

Je développe ma propre gemme d'analyseur d'options appelée Acclaim .

Je l'ai écrit parce que je voulais créer des interfaces de ligne de commande de style git et pouvoir séparer proprement les fonctionnalités de chaque commande en classes distinctes, mais il peut également être utilisé sans le cadre de commande complet:

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

Aucune version stable pour le moment, mais j'ai déjà implémenté certaines fonctionnalités telles que:

  • analyseur d'options personnalisé
  • analyse flexible des arguments d'option qui permet à la fois un minimum et un optionnel
  • prise en charge de nombreux styles d'options
  • remplacer, ajouter ou augmenter sur plusieurs instances de la même option
  • gestionnaires d'options personnalisées
  • gestionnaires de types personnalisés
  • gestionnaires prédéfinis pour les classes de bibliothèque standard courantes

L'accent est mis sur les commandes, donc cela peut être un peu lourd pour une simple analyse de ligne de commande, mais cela fonctionne bien et je l'ai utilisé sur tous mes projets. Si vous êtes intéressé par l'aspect de l'interface de commande, consultez la page GitHub du projet pour plus d'informations et d'exemples.

Matheus Moreira
la source
1
Je recommande vivement Acclaim. Il est facile à utiliser et dispose de toutes les options dont vous avez besoin.
bowsersenior le
0

Supposons qu'une commande ait au plus une action et un nombre arbitraire d'options comme ceci:

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

L'analyse sans validation peut être comme ceci:

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

if ACTION == '***'
  ...
  if OPTIONS.include? '*'
    ...
  end
  ...
end
Bohr
la source
0

https://github.com/soveran/clap

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

46LOC (à 1.0.0), aucune dépendance à l'analyseur d'options externe. Fait le travail. Probablement pas aussi complet que d'autres, mais c'est 46LOC.

Si vous vérifiez le code, vous pouvez facilement dupliquer la technique sous-jacente - attribuez des lambdas et utilisez l'arity pour vous assurer que le nombre correct d'arguments suit l'indicateur si vous ne voulez vraiment pas de bibliothèque externe.

Facile. Pas cher.


EDIT : le concept sous-jacent se résume car je suppose que vous pouvez le copier / coller dans un script pour créer un analyseur de ligne de commande raisonnable. Ce n'est certainement pas quelque chose que je consacrerais à la mémoire, mais utiliser la lambda arity comme un analyseur bon marché est une idée nouvelle:

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...
Ben Alavi
la source
Veuillez lire le point 1 à la fin de la question. Si vous ne pouvez pas saisir tout le code nécessaire directement dans votre réponse ici, ce n'est pas une réponse à la question.
cjs
Bon point! Je pense qu'à l'époque, j'ai supposé que la bibliothèque était suffisamment petite pour que vous puissiez copier / coller le tout dans n'importe quel script sur lequel vous travailliez sans avoir besoin d'une dépendance externe, mais ce n'est certainement pas une ligne unique que je consacrerais à la mémoire pour remplir votre point n ° 2. Je pense que le concept sous-jacent est assez nouveau et cool pour que je suis allé de l'avant et en ai fait une version résumée qui répond à votre question de manière un peu plus appropriée.
Ben Alavi le
-1

Je vais partager mon propre analyseur d'options simple sur lequel je travaille depuis un certain temps. Il ne s'agit que de 74 lignes de code, et il fait les bases de ce que fait l'analyseur d'options interne de Git. J'ai pris OptionParser comme inspiration, ainsi que celle de Git.

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

Cela ressemble à ceci:

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
FelipeC
la source
Vous n'avez même pas vérifié le code. J'ai mis une autre réponse en supprimant le code d'analyse.
FelipeC
Je n'avais pas besoin de dire que vous aviez dit que c'était 74 lignes. Cependant, je viens de l'examiner maintenant et cela viole toujours la première phrase de l'exigence 2. (Cette réponse viole également la convention Stack Overflow selon laquelle vous devez inclure le code dans votre réponse plutôt que de donner un lien hors site.)
cjs
-1

EasyOptions ne nécessite aucun code d'analyse d'option. Écrivez simplement le texte d'aide, exigez, c'est fait.

## 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
Renato Silva
la source
EasyOptions est un fichier Ruby unique sans instructions require, et il n'y a aucun code d'analyse à retenir. Il semble que vous vouliez plutôt quelque chose d'intégrable qui soit assez puissant mais simple à retenir.
Renato Silva