Rubyでの非常に安価なコマンドラインオプションの解析


114

編集:お願い、お願いお願い応答する前に、この記事の一番下にリストされている2つの要件をお読みください。人々は新しい宝石やライブラリなどを投稿し続けますが、それらは明らかに要件を満たしていません。

コマンドラインオプションをシンプルなスクリプトに非常に安価にハックしたい場合があります。getoptsや解析などを行わずにそれを行う楽しい方法は次のとおりです。

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

" myprog -i foo bar -q"のようにオプション以外のコマンドラインパラメーターを受け入れるため、これは通常のUnixオプション構文ではありませんが、それでも問題ありません。(Subversion開発者などの一部の人はこれを好みます。時には私もそうします。)

存在するオプションまたは存在しないオプションは、上記よりも簡単に実装することはできません。(1つの割り当て、1つの関数呼び出し、1つの副作用。) " -f filename」?

編集:

ライブラリが「1つの[800行]ファイルに収まる」とTrollopの作者が言うまで私には明確にならなかったので、私が以前に触れなかった1つの点は、私がクリーンなだけでなく探しているということです。構文ですが、次の特性を持つ手法の場合:

  1. コード全体をスクリプトファイルに含めることができ(実際のスクリプト自体を圧倒することなく、これは数十行になる場合があります)、bin標準のRuby 1.8を搭載した任意のシステムのディレクトリに単一のファイルをドロップできます。。[5-7]インストールして使用します。requireステートメントのないRubyスクリプトを記述できず、いくつかのオプションを解析するコードが数十行以下の場合、この要件は満たされません。

  2. コードは小さくてシンプルなので、他の場所からカットアンドペーストするのではなく、トリックを実行するコードを直接入力するのに十分なほど覚えています。ファイアウォールで保護されたサーバーのコンソール上でインターネットにアクセスできない状況で、クライアントが使用する簡単なスクリプトを一緒に投げたいと考えているとします。私はあなたのことは知りませんが、(上記の要件を満たしていないことを除いて)45行の簡略化されたmicro-optparseを記憶することは、私がやりたいことではありません。


2
getoptlongに対する異論に興味があるだけですか?
マークキャリー

それの冗長性。getoptlogを使用すると、オプションの解析コードが、実際に機能するスクリプトの部分よりも長くなることがあります。これは見た目の美しさだけでなく、維持費の問題でもあります。
cjs

8
私は、スクリプトを含める要件を理解していない-の両方getoptlongoptparseあなたのスクリプトを展開する際にそれらをコピーする必要はありませんので、標準のRubyライブラリにある-ルビーは、その後、そのマシン上で動作するかどうrequire 'optparse'か、require 'getoptlong'あまりにも動作しますが。
ランピオン

参照stackoverflow.com/questions/21357953/...だけでなく、娼妓について以下のウィリアム・モーガンの答えを。
fearless_fool 2014年

@CurtSampson何人の人があなたの質問に答えなかったのか信じられません。いずれにしても、XD XDの3件の投稿について、ようやく良い回答が得られました
OneChillDude 2016年

回答:


235

Trollopの作者として、私は人々がオプションパーサーで合理的であると考えるものをことはできません。真剣に。それは心を揺さぶる。

オプションを解析するために他のモジュールを拡張するモジュールを作成する必要があるのはなぜですか?なぜ何かをサブクラス化する必要があるのですか?コマンドラインを解析するためだけに「フレームワーク」をサブスクライブする必要があるのはなぜですか?

上記のTrollopバージョンは次のとおりです。

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

以上です。opts今キーを持つハッシュである:quiet:interactive:filename。あなたはそれで好きなことをすることができます。画面の幅に合わせてフォーマットされた美しいヘルプページ、自動の短い引数名、タイプチェックなど、必要なものがすべて表示されます。

これは1つのファイルなので、正式な依存関係が必要ない場合は、lib /ディレクトリにドロップできます。それは簡単に拾うことができる最小限のDSLを持っています。

オプションの人あたりのLOC。それは重要です。


39
ところで、Trollop(既にここで言及されています)を書いたことに対して+1ですが、最初の段落を少し控えめにしても構いません。
cjs 2009年

33
この場合、彼は文句を言う権利があります。代替案を見ると、[1 ] [2 ] [3 ]は、基本的に単純な文字列配列を処理しているだけなので(実際には、それをシンクさせます)、なぜ疑問に思わざるを得ないのでしょうか。あの膨らみから何を得るのですか?これはCではなく、文字列が「問題あり」です。もちろん、それぞれ自分自身に。:)
srcspider 2010

50
これを少しトーンダウンしないでください。それは正義の長男です、兄弟。
William Pietri

7
10番目の単語を少し下げてください。
Andrew Grimm、

3
Trollopの+1。私はそれを私のテスト自動化システムに使用し、それはちょうど機能します。さらに、コーディングがとても簡単なので、バナーの喜びを体験するためだけにバナーを並べ替えることもあります。
kinofrost 2011

76

私はあなたのために嫌悪感を共有するrequire 'getopts'主な原因である素晴らしさに、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
おかげで、これがどのようにOP要求に適合しないのか、特に標準ライブラリのすべてを考慮すると、非標準コードのインストール/ベンダリングの必要性と比較してわかりません
dolzenko

3
これは、追加のファイルを必要としないことを除いて、trollopバージョンと同じように見えます。
Claudiu

59

これが私が通常使う標準的なテクニックです:

#!/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
質問に答えますが、Trollopの方がはるかに扱いやすいようです。既製のホイールが非常に滑らかなのに、なぜホイールを再発明するのですか?
Mikey TK、

7
既製のホイールは滑らかではありません。要件に注意して、もう一度質問をよく読んでください。
cjs

2
+1 Trollopのような他の依存関係を使用したくない、または単に使用できないため、ホイールを再発明する必要がある場合があります。
lzap 14

TrollopをGemとしてインストールする必要はありません。libフォルダーまたはコードにファイルを1つドロップするだけで、rubymemに触れることなく使用できます。
オーバーブリード

私にとっては、変更when /^-/ then usage("Unknown option: #{ARGV[0].inspect}")するwhen /^-/ then usage("Unknown option: #{ARGV.shift.inspect}")必要がありました。そうしないと、無限の使用ループに陥ります
ケーシー

36

誰も言及していないようで、タイトル安価なコマンドライン解析を指しているので、Rubyインタープリターに作業を任せてみませんか?-s(たとえば、シバンで)スイッチを渡すと、1文字のグローバル変数に割り当てられた、ごく単純なスイッチが無料で手に入ります。このスイッチを使用した例を次に示します。

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

そして、これを保存して./testchmodしたときの出力は+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"]

詳細ruby -hについては、を参照してください。

それできるだけ安くなければなりません。次のようなスイッチを試すと、NameErrorが発生します。-:そこにいくつかの検証があります。もちろん、スイッチ以外の引数の後にスイッチを配置することはできませんが、特別なことが必要な場合は、最小限のOptionParserを使用する必要があります。実際、この手法について私を悩ます唯一のことは、未設定のグローバル変数にアクセスすると警告が表示されることです(有効にした場合)が、それでもまだ誤っているため、使い捨てツールとクイックスクリプト。

Rubyで非常に安価なコマンドラインオプションを解析する方法」のコメントでFelipeCが指摘した警告の1つは、シェルが3トークンシバンをサポートしていない可能性があることです。/usr/bin/env ruby -wRubyへの実際のパス(など/usr/local/bin/ruby -w)に置き換えるか、ラッパースクリプトなどから実行する必要がある場合があります。


2
ありがとう:)彼がこの2年間この答えを待っていなかったと思います。
DarkHeart 2013年

3
私は確かにこの2年間この答えを待っていました。:-)より真剣に、これは私が探していた一種の賢い考えです。警告は少し迷惑ですが、それを緩和する方法を考えることができます。
cjs

@ CurtSampson、MRIのフラグは(最終的に)助けてくれてうれしいです。MRIのフラグはPerlから直接取り除かれ、シェルのワンライナーで不当に使用される傾向があります。答えがまだあなたに役立つなら、遠慮なく受け入れてください。:)
bjjb 14

1
Linuxのシバンで複数の引数を使用することはできません。/ usr / bin / env: 'ruby -s':No
such

13

私はmicro-optparseを作成して、短くて使いやすいoption-parserのこの明白なニーズを満たすようにしました。Trollopに似た構文を持ち、70行短いです。検証が不要で、空の行なしで実行できる場合は、45行に減らすことができます。それがまさにあなたが探していたものだと思います。

短い例:

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!

-hまたはで--help印刷するスクリプトを呼び出す

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

入力がデフォルト値と同じタイプかどうかをチェックし、短いアクセサと長いアクセサを生成し、無効な引数が指定された場合は説明的なエラーメッセージを出力します。

はいくつかのオプションパーサー比較し、それぞれのオプションパーサーを使用して問題を解決しました。これらの例と私の要約を使用して、有益な決定を行うことができます。リストに実装を追加してください。:)


ライブラリ自体は素晴らしいかもしれないようです。ただし、行数をTrollopと比較することは、optparse1937行に依存する(必要とする)必要があるため、不誠実ではありません。
Telemachus

6
optparseはデフォルトのライブラリなので、行数を比較しても問題ありません。つまり、すべてのRubyインストールに同梱されています。Trollopはサードパーティのライブラリであるため、プロジェクトに含めるたびに完全なコードをインポートする必要があります。µ-optparse optparseはすでに存在しているため、常に〜70行しか必要としません。
フロリアンピルツ

8

なぜあなたがoptparseを避けたいのか私は完全に理解しています-それはあまりにも多くなるかもしれません。しかし、ライブラリとして提供される(OptParseと比べて)はるかに「軽い」ソリューションがいくつかありますが、単一のgemインストールを価値あるものにするのに十分なほど単純です。

たとえば、このOptiFlagの例を確認してください。処理のためのほんの数行。ケースに合わせて少し切り詰めた例:

require 'optiflag'

module Whatever extend OptiFlagSet
  flag "f"
  and_process!
end 

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

ありすぎて、カスタマイズの例のトンが。もっと簡単な別の方法を使用したことを思い出しましたが、今のところそれは脱出していますが、見つかったらここに戻ってコメントを追加します。


明確化された質問により適するように、回答を自由に編集してください。
cjs

4

これは私が本当に、本当に安い引数に使用するものです:

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

main

したがって、実行programname foo barするとfooが呼び出され、次にbarが呼び出されます。使い捨てスクリプトに便利です。


3

あなたは次のようなことを試すことができます:

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

3

トールをwycatsで検討しましたか?optparseよりもずっときれいだと思います。すでにスクリプトを作成している場合は、それをフォーマットしたり、リファクタリングしたりするのに少し手間がかかるかもしれませんが、オプションの処理が非常に簡単になります。

以下は、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はコマンドを次のように自動的にマッピングします。

app install myname --force

これは次のように変換されます:

MyApp.new.install("myname")
# with {'force' => true} as options hash
  1. Thorから継承してクラスをオプションマッパーに変換する
  2. 追加の無効な識別子を特定のメソッドにマップします。この場合、-Lを:listに変換します
  3. すぐ下に方法を記述します。最初のパラメーターは使用法情報で、2番目のパラメーターは説明です。
  4. 追加のオプションを提供します。これらは-および-paramsからマーシャリングされます。この場合、-forceおよび-fオプションが追加されます。

たくさんのサブコマンドを含む単一のバイナリはよくやるので、コマンドマッピングのことが好きです。それでも、「光」からの道を進んでいますが。同じ機能を表現するさらに簡単な方法を見つけられませんか?--help出力を印刷する必要がない場合はどうなりますか?「head myprogram.rb」がヘルプ出力である場合はどうなりますか?
cjs 2009年

3

これが私のお気に入りの汚いオプションパーサーです。

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

オプションは正規表現であるため、「-h」は「--help」にも一致します。

読みやすく、覚えやすく、外部ライブラリはなく、最小限のコード。


はい、そうです。それが問題である場合は、さらに正規表現を追加できます。たとえば/-h(\b|elp)
EdwardTeach

2

Trollopはかなり安いです。


それは、< trollop.rubyforge.org >です。図書館を探していたわけではありませんが、むしろ好きです。
cjs 2009年

確かに、それは図書館です。ただし、LOCが800未満の場合、それはごくわずかです。gitorious.org/trollop/mainline/blobs/master/lib/trollop.rb
g33kz0r

1
「図書館」を使うのであれば、30行から50行がいいと思いました。しかし、繰り返しになりますが、コードが満載された別のファイルに到達すると、APIの設計は行数よりも重要になると思います。それでも、ランダムシステムのbinディレクトリに配置したい1回限りのスクリプトに含めたいかどうかはわかりません。
cjs 2009年

1
あなたはそれを逆に持っています:ポイントは、より複雑な展開戦略を持つ必要がないようにすることです。
cjs 2012

1
質問をした人のニーズと意図を完全に誤解している(または無視している)ため、あなたはまったく間違っています。質問、特に最後の2つの点を注意深く読み直すことをお勧めします。
cjs 2012

2

gemを使用せずにキー/値コマンドの単純なコマンドラインパーサーが必要な場合:

ただし、これは常にキーと値のペアがある場合にのみ機能します。

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

チェックが必要ない場合は、次のように使用できます。

opts = {} 

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

2

これが、ほとんどのスクリプトの上部で使用するコードスニペットです。

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

また、汚いスクリプトで追加のファイルを要求することも嫌いです。私の解決策は、あなたが求めているものに非常に近いものです。コマンドラインを解析して位置引数を固定し、ハッシュオブジェクト(通常は、arghashという名前のオブジェクトに割り当てられている)に切り替える、スクリプトの先頭に10行のコードスニペットを貼り付けます、以下の例でます。

解析するコマンドラインの例を次に示します...

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

このようなハッシュになります。

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

さらに、2つの便利なメソッドがハッシュに追加されています。

  • argc() 非スイッチ引数の数を返します。
  • switches() 存在するスイッチのキーを含む配列を返します

これは、次のような迅速で汚いものを許可することを意味します...

  • 渡されたスイッチに関係なく、位置引数の数が正しいことを確認します( arghash.argc == 2)で
  • 位置引数の前または間にあるスイッチに関係なく、相対位置で位置引数にアクセスします(たとえば、arghash[1]常に2番目の非スイッチ引数を取得します)。
  • 「--max = 15」などのコマンドラインで値が割り当てられたスイッチをサポートします。arghash['--max=']これにより、コマンドラインの例で「15」の値が得られます。
  • スイッチが存在する場合はarghash['-s']true、存在しない場合はnilと評価するなどの非常に単純な表記法を使用して、コマンドラインでスイッチの有無をテストします。
  • 次のような集合演算を使用して、スイッチまたはスイッチの代替の存在をテストします。

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

  • 次のような集合演算を使用して無効なスイッチの使用を識別します

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

  • Hash.merge()設定されていない場合は-max =の値を入力し、渡されなかった場合は4番目の位置引数を追加する次の例のような簡単な方法を使用して、欠落している引数のデフォルト値を指定します。

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


(私はこれを編集してコードのフォーマットを改善しました。主に整列を使用してブロックと制御構造をより明確にします。これは句読点が多いこのようなもので特に重要だと思います。しかし、新しいフォーマットが嫌いなら、遠慮なくどうぞ編集を元に戻します。)
cjs 2018

これは、世界で最も簡単に読めるものではないにしても、非常に便利です。引数の「構文」を変更すると(ここでは、位置引数の後にオプションを許可し、を使用しない限りオプションの引数を許可しない=)、必要なコードに違いが生じることもわかります。
cjs 2018

再フォーマットしていただきありがとうございます。読むことは間違いなくあいまいであり、明確にするためにコードの長さを簡単に交換できます。このコードを信頼するようになったので、多かれ少なかれ、それを宝石のように扱い、それが何をしているのかを内部で理解しようとすることはありません(そのため、信頼性が高まったため、明快さはもはや重要ではなくなりました)。
デビッドフォスター

1

これは受け入れられた回答と非常に似ていますが、ARGV.delete_if私が使用しているのは、単純なパーサーで使用する方法です。唯一の真の違いは、引数付きのオプションは一緒になければならないということです(例:)-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

どうやら@WilliamMorganと私は似ていると思います。昨夜Githubにリリースしたばかりです。GithubでOptionParserを検索した後、Trollopと同様のライブラリ(名前は?)が表示されるようになりました。スイッチを参照してください。

いくつかの違いがありますが、哲学は同じです。明らかな違いの1つは、SwitchesがOptionParserに依存していることです。


0

Acclaimという独自のオプションパーサーgemを開発しています

gitスタイルのコマンドラインインターフェイスを作成し、各コマンドの機能を個別のクラスに明確に分離できるようにしたかったので、それを記述しましたが、コマンドフレームワーク全体がなくても使用できます。

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

現時点では安定版リリースはありませんが、次のような機能をすでに実装しています。

  • カスタムオプションパーサー
  • 最小とオプションの両方を可能にするオプションの引数の柔軟な解析
  • 多くのオプションスタイルのサポート
  • 同じオプションの複数のインスタンスを置換、追加、または発生させる
  • カスタムオプションハンドラー
  • カスタム型ハンドラー
  • 共通標準ライブラリクラスの定義済みハンドラ

コマンドには多くの重点が置かれているため、単純なコマンドライン解析では少し重いかもしれませんが、うまく機能し、すべてのプロジェクトで使用しています。コマンドインターフェースの側面に興味がある場合は、プロジェクトのGitHubページで詳細と例を確認してください


1
Acclaimを強くお勧めします。使いやすく、必要なすべてのオプションがあります。
bowsersenior 2012

0

コマンドに次のような最大1つのアクションと任意の数のオプションがあるとします。

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

検証なしの解析は次のようになります。

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(1.0.0)、外部オプションパーサーに依存しません。仕事を終わらせます。おそらく他のものほどフル機能ではありませんが、46LOCです。

コードをチェックすると、基礎となる手法をかなり簡単に複製できます。ラムダを割り当て、アリティを使用して、本当に外部ライブラリが必要ない場合は、適切な数の引数がフラグに続くようにします。

シンプル。安いです。


編集:合理的なコマンドラインパーサーを作成するためにスクリプトにコピー/貼り付けする可能性があると思うので、根底にあるコンセプトを煮詰めました。それは間違いなく私がメモリにコミットするものではありませんが、ラムダアリティを安価なパーサーとして使用することは斬新なアイデアです。

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...

質問の最後のポイント1をお読みください。ここで答えに必要なコードをすべて入力できない場合、それは質問に対する答えではありません。
cjs 2018

いい視点ね!当時、私はlibが小さいので、外部依存関係を必要とせずに作業しているスクリプトにすべてをコピー/貼り付けできると思っていましたが、メモリにコミットする明確な1行ではありませんあなたのポイント#2を満たすために。根底にあるコンセプトは斬新でクールだと思います。私が先に進んで、あなたの質問にもう少し適切に答える煮詰めバージョンを作りました。
Ben Alavi

-1

しばらく取り組んできた独自のシンプルなオプションパーサーを共有します。わずか74行のコードで、Gitの内部オプションパーサーが行うことの基本を実行します。OptionParserとGitをインスピレーションとして使用しました。

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

次のようになります。

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 2018年

74行だったと言ってもらえませんでした。ただし、今見たところ、まだ要件2の最初の文に違反しています(この応答は、オフサイトリンクではなく、コードを回答に含める必要があるというスタックオーバーフローの規則にも違反しています。)
cjs 2018年

-1

EasyOptionsは、コードを解析するオプションをまったく必要としません。ヘルプテキストを書いて、要求し、完了します。

## 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はrequireステートメントのない単一のRubyファイルであり、覚えておくべき解析コードはまったくありません。代わりに、強力でありながら覚えやすいシンプルな埋め込み可能なものが欲しいようです。
Renato Silva
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.