Ruby'de Gerçekten Ucuz Komut Satırı Seçeneği Ayrıştırma


114

DÜZENLEME: Lütfen lütfen , lütfen cevap vermeden önce bu yazının altındaki listelenen iki gereksinimleri okumak. İnsanlar yeni mücevherlerini, kitaplıklarını falan yayınlamaya devam ediyor, bu da açıkça gereksinimleri karşılamıyor.

Bazen bazı komut satırı seçeneklerini çok ucuza basit bir betiğe dönüştürmek istiyorum. Getopts, ayrıştırma veya benzeri bir şeyle uğraşmadan bunu yapmanın eğlenceli bir yolu şudur:

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

Bu tam olarak normal Unix seçenekleri sözdizimi değil, çünkü " myprog -i foo bar -q" seçeneğindeki gibi seçenek olmayan komut satırı parametrelerini kabul edecek , ancak bununla yaşayabilirim. (Subversion geliştiricileri gibi bazı insanlar bunu tercih ediyor. Bazen ben de tercih ediyorum.)

Sadece mevcut olan veya olmayan bir seçenek yukarıdakilerden çok daha basit bir şekilde uygulanamaz. (Tek atama, tek işlev çağrısı, tek yan etki.) " -f Dosya adı " gibi bir parametre alan seçenekleri ele almanın eşit derecede basit bir yolu var mı ?

DÜZENLE:

Daha önce belirtmediğim bir nokta, çünkü Trollop'un yazarı kitaplığın "tek bir [800 satırlık] dosyaya" sığdığından söz edene kadar benim için netleşmemişti, sadece temiz sözdizimi, ancak aşağıdaki özelliklere sahip bir teknik için:

  1. Kodun tamamı komut dosyasına dahil edilebilir (gerçek betiğin kendisini ezmeden, ki bu sadece birkaç düzine satır olabilir), böylece binstandart Ruby 1.8 ile herhangi bir sistemdeki bir dizine tek bir dosya bırakılabilir. . [5-7] kurulum ve kullanma. Require ifadesi içermeyen ve birkaç seçeneği ayrıştıracak kodun bir düzine satırın altında olduğu bir Ruby betiği yazamazsanız, bu gereksinimi karşılayamazsınız.

  2. Kod, başka bir yerden kesip yapıştırmak yerine, hile yapacak kodu doğrudan yazmak için yeterince küçük ve basittir. İnternet erişimi olmayan, güvenlik duvarlı bir sunucunun konsolunda olduğunuz ve bir istemcinin kullanması için hızlı bir komut dosyasını bir araya getirmek istediğiniz durumu düşünün. Sizi bilmiyorum, ancak (yukarıdaki gerekliliği yerine getirmemenin yanı sıra) 45 satırlık basitleştirilmiş mikro-optiği ezberlemek bile yapmayı umursadığım bir şey değil.


2
Getoptlong'a yapılan itirazı merak mı ediyorsunuz?
Mark Carey

Ayrıntıları. Getoptlog ile, bazen kodu çözümleme seçenekleri, betiğin işi gerçekten yapan kısmından daha uzundur. Bu sadece estetik bir sorun değil, aynı zamanda bir bakım maliyeti sorunudur.
cjs

8
Her iki - Senaryonun içerme şartını anlamıyorum getoptlongve optparseyakut sonra, bu makinede çalışıyorsa - Eğer senaryoyu dağıtırken onları kopyalamak için gerek kalmaz, standart yakut kütüphanesinde olan require 'optparse'veya require 'getoptlong'çok çalışacaktır.
rampion

Bkz stackoverflow.com/questions/21357953/... yanı sıra Trollop hakkında aşağıda William Morgan cevabı.
fearless_fool

@CurtSampson Sorunuza kaç kişinin cevap vermediğine inanamıyorum. Her iki durumda da, sonunda XD
XD'den sonraki

Yanıtlar:


235

Trollop'un yazarı olarak , insanların bir seçenek çözümleyicide makul olduğunu düşündükleri şeylere İNANAMAM . Ciddi anlamda. Zihni şaşırtıyor.

Neden başka bir modülü seçenekleri ayrıştırmak için genişleten bir modül yapmalıyım? Neden herhangi bir şeyi alt sınıflara ayırmalıyım? Komut satırını ayrıştırmak için neden bazı "çerçevelere" abone olmalıyım?

İşte yukarıdakilerin Trollop versiyonu:

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

Ve bu kadar. optsartık anahtarları :quietolan bir karma :interactiveve :filename. Onunla ne istersen yapabilirsin. Ve ekran genişliğinize uyacak şekilde biçimlendirilmiş güzel bir yardım sayfası, otomatik kısa argüman adları, yazım denetimi ... ihtiyacınız olan her şey.

Bu tek bir dosyadır, eğer resmi bir bağımlılık istemiyorsanız onu lib / dizininize bırakabilirsiniz. Alınması kolay minimal bir DSL'ye sahiptir.

Seçenek kişi başına LOC. Fark eder, önemi var.


39
BTW, +1 Trollop'u yazdığım için (burada daha önce bahsedilmişti), ancak ilk paragrafı biraz hafifletmekten çekinmeyin.
cjs

33
Korkarım bu durumda şikayet etmeye hakkı var. Alternatiflere baktığınızda: [1 ] [2 ] [3 ], temelde basit bir dizgi dizisini işlemekten ibaret olan şey için (gerçekten hayır, içeri girmesine izin verin), yardım edemezsiniz ama NEDEN merak edersiniz? Tüm bu şişkinlikten ne kazanıyorsun? Bu, dizelerin "sorunlu" olduğu C değildir. Tabii ki her biri kendi başına. :)
srcspider

50
Lütfen bunu biraz azaltmayın. Bu doğru bir şap, kardeşim.
William Pietri

7
Onuncu kelimeyi biraz hafifletmekten çekinmeyin.
Andrew Grimm

3
Trollop için +1. Test otomasyon sistemim için kullanıyorum ve Just Works. Üstelik kodlamak o kadar kolay ki bazen afişimi sırf neşesini yaşamak için yeniden düzenliyorum.
kinofrost

76

Hoşnutsuzluğunuzu paylaşıyorum require 'getopts', esas olarak şu muhteşemlik nedeniyle 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
Teşekkürler, standart olmayan herhangi bir kodun yüklenmesi /
satılması

3
bu, ekstra dosyaya ihtiyaç duymaması dışında trollop versiyonuna benzer.
Claudiu

59

İşte genellikle kullandığım standart teknik:

#!/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
Soruyu yanıtlıyor ama adamım, Trollop'un üstesinden gelmek çok daha kolay görünüyor. Önceden hazırlanmış tekerlek çok daha pürüzsüzken neden tekerleği yeniden icat edesiniz?
Mikey TK

7
Önceden yapılmış tekerlek daha pürüzsüz değil. Gereksinimlere dikkat ederek soruyu tekrar dikkatlice okuyun.
cjs

2
Sadece yok istiyorum ya çünkü 1 Bazen, tekerleği yeniden icat gerek edemez Trollop gibi diğer bağımlılıklar kullanın.
lzap

Trollop'un mücevher olarak kurulmasına gerek yoktur. libKlasörünüze veya kodunuza bir dosya bırakabilir ve rubygems'e dokunmadan bile kullanabilirsiniz.
Overbryd

Benim için değiştirmem when /^-/ then usage("Unknown option: #{ARGV[0].inspect}")gerekiyordu when /^-/ then usage("Unknown option: #{ARGV.shift.inspect}")yoksa sonsuz bir kullanım döngüsüne
casey

36

Kimse söz göründü ve başlık yana gelmez bakın ucuz komut satırı ayrıştırma, neden sadece sizin için işi yapmak tercüman Ruby izin? -sAnahtarı geçerseniz (örneğin, sizin yazınızda), tek harfli global değişkenlere atanmış, kir-basit anahtarları ücretsiz olarak alırsınız. İşte bu anahtarı kullanan örneğiniz:

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

Ve bunu farklı kaydettiğimde ./testve değiştirdiğimde çıkan çıktı +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"]

Ayrıntılar ruby -hiçin bakın.

Bu zorunluluk olabildiğince ucuz . Gibi bir anahtarı denerseniz bir NameError yükseltecektir.-: , bu yüzden orada bazı doğrulama var. Elbette, geçişsiz bir argümandan sonra herhangi bir anahtarınız olamaz, ancak süslü bir şeye ihtiyacınız varsa, gerçekten minimum OptionParser kullanmalısınız. Aslında, bu teknikle ilgili beni rahatsız eden tek şey, ayarlanmayan bir global değişkene erişirken (etkinleştirdiyseniz) bir uyarı alacağınızdır, ancak yine de yanlıştır, bu nedenle atılabilir araçlar ve hızlı Kodlar.

FelipeC'nin " Ruby'de gerçekten ucuz komut satırı seçeneği nasıl ayrıştırılır " yazısındaki yorumlarda dikkat çektiği bir uyarı, kabuğunuzun 3 simgeli shebang'ı desteklemeyebileceğidir; /usr/bin/env ruby -wRuby'nize giden gerçek yolu (gibi /usr/local/bin/ruby -w) değiştirmeniz veya bir sarmalayıcı komut dosyasından veya başka bir şeyle çalıştırmanız gerekebilir .


2
Teşekkürler :) Umarım son iki yıldır bu cevabı beklememiştir.
DarkHeart

3
Gerçekten de son iki yıldır bu cevabı bekliyorum. :-) Daha ciddisi, aradığım türden zekice bir düşünce. Uyarı meselesi biraz can sıkıcı, ancak bunu azaltmanın yollarını düşünebilirim.
cjs

(Sonunda) yardımcı olabildiğime sevindim, @CurtSampson, MRI'ın bayrakları, tek astarlı kabuklarda gereksiz yere kullanılma eğiliminde oldukları Perl'den çıkarıldı. Cevap sizin için hala yararlıysa, kabul etmekten çekinmeyin. :)
bjjb

1
Linux'ta bir shebang'da birden fazla argüman kullanamazsınız. / usr / bin / env: 'ruby -s': Böyle bir dosya veya dizin yok
FelipeC

13

Ben inşa mikro optparse kısa bu bariz ihtiyaç doldurmak için, ama kolay bir seçenek-ayrıştırıcı kullanmak. Trollop'a benzer bir sözdizimi vardır ve 70 satır kısadır. Doğrulamalara ihtiyacınız yoksa ve boş satırlar olmadan yapabiliyorsanız, 45 satıra kadar kesebilirsiniz. Sanırım tam da aradığın buydu.

Kısa örnek:

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!

Komut dosyasını çağırmak -hveya --helpyazdırmak

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

Girişin varsayılan değerle aynı türde olup olmadığını kontrol eder, kısa ve uzun erişimciler üretir, geçersiz bağımsız değişkenler verilirse açıklayıcı hata mesajları yazdırır ve daha fazlasını yapar.

Ben birkaç seçenek-ayrıştırıcı karşılaştırıldığında vardı problemi için her seçenek-ayrıştırıcı kullanarak. Bilgilendirici bir karar vermek için bu örnekleri ve özetimi kullanabilirsiniz. Listeye daha fazla uygulama eklemekten çekinmeyin. :)


Kütüphanenin kendisi harika gibi görünüyor. Bununla birlikte, optparsehangisinin 1937 satırına bağlı olduğu ve gerektirdiği için satır sayılarını Trollop ile karşılaştırmak samimiyetsiz değil mi ?
Telemachus

6
Satır sayımlarını karşılaştırmak kesinlikle uygundur, çünkü optparsevarsayılan bir kitaplıktır, yani her Ruby kurulumuyla birlikte gelir. Trollopbir üçüncü taraf kitaplığıdır, bu nedenle bir projeye dahil etmek istediğiniz her seferinde kodun tamamını içe aktarmanız gerekir. µ-optparse her zaman ~ 70 satır gerektirir, çünkü optparsezaten vardır.
Florian Pilz

8

İyimser olmaktan neden kaçınmak istediğinizi tamamen anlıyorum - çok fazla olabilir. Ancak kitaplık olarak gelen ancak tek bir mücevher kurulumunu değerli kılacak kadar basit olan birkaç "daha hafif" çözüm (OptParse ile karşılaştırıldığında) vardır.

Örneğin, bu OptiFlag örneğine bakın . İşleme için sadece birkaç satır. Durumunuza uyarlanmış, biraz kısaltılmış bir örnek:

require 'optiflag'

module Whatever extend OptiFlagSet
  flag "f"
  and_process!
end 

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

Orada özelleştirilmiş örneklerinden ton da . Daha da kolay olan başka birini kullandığımı hatırlıyorum, ama şimdilik benden kaçtı ama geri gelip bulursam buraya bir yorum ekleyeceğim.


Açıklanan soruya daha iyi uyması için cevabınızı düzenlemekten çekinmeyin.
cjs

4

Gerçekten ucuz argümanlar için kullandığım şey bu:

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

main

yani çalıştırırsanız programname foo barfoo ve ardından bar'ı çağırır. Kullanılmayan komut dosyaları için kullanışlıdır.


3

Şunun gibi bir şey deneyebilirsiniz:

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

3

Eğer kabul mü Thor wycats tarafından? Optparse'den çok daha temiz olduğunu düşünüyorum. Zaten yazılmış bir betiğiniz varsa, onu formatlamak veya thor için yeniden düzenlemek biraz daha çalışma olabilir, ancak bu, işleme seçeneklerini çok basit hale getirir.

README'den örnek pasaj:

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, komutları şu şekilde otomatik olarak eşler:

app install myname --force

Bu şuna dönüştürülür:

MyApp.new.install("myname")
# with {'force' => true} as options hash
  1. Bir sınıfı bir seçenek eşleştiricisine dönüştürmek için Thor'dan devralın
  2. Ek geçerli olmayan tanımlayıcıları belirli yöntemlerle eşleyin. Bu durumda, -L'yi listeye dönüştürün
  3. Hemen aşağıdaki yöntemi açıklayın. İlk parametre kullanım bilgisidir ve ikinci parametre açıklamadır.
  4. Herhangi bir ek seçenek sağlayın. Bunlar - ve - parametrelerinden sıralanacaktır. Bu durumda, bir --force ve a -f seçeneği eklenir.

Bir grup alt komuta sahip tek bir ikili dosya sık yaptığım bir şey olduğundan, komut eşleme işini seviyorum. Yine de, 'ışıktan' uzaklaşmış olmanıza rağmen Aynı işlevi ifade etmenin daha da basit bir yolunu bulabilir misiniz? Ya --helpçıktı yazdırmanız gerekmediyse ? Ya yardım çıktısı "head myprogram.rb" ise?
cjs

3

İşte en sevdiğim hızlı ve kirli seçenek ayrıştırıcım:

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

Seçenekler normal ifadelerdir, dolayısıyla "-h" ayrıca "--help" ile de eşleşir.

Okunabilir, hatırlaması kolay, harici kitaplık yok ve minimum kod.


Evet, olurdu. Bu bir sorunsa daha fazla normal ifade ekleyebilirsiniz, örneğin/-h(\b|elp)
EdwardTeach

2

Trollop oldukça ucuz.


Bu < trollop.rubyforge.org > olacaktır. Aslında bir kütüphane aramamama rağmen hoşuma gitti.
cjs

Doğru, bu bir kitaplık. Ancak, <800 LOC'de, bu oldukça ihmal edilebilir bir durumdur. gitorious.org/trollop/mainline/blobs/master/lib/trollop.rb
g33kz0r

1
Bir "kütüphane" kullanacak kadar ileri gidersem, 30-50 satırın iyi olacağını düşünüyordum. Ama yine de, kodla dolu ayrı bir dosyaya sahip olduğunuzda, API tasarımı satır sayısından daha önemlidir. Yine de, rastgele bir sistemde bin dizinine eklemek istediğim tek seferlik bir komut dosyasına dahil etmek isteyeceğimden emin değilim.
cjs

1
Geriye doğru anladınız: önemli olan, daha karmaşık bir dağıtım stratejisine sahip olmaktan kaçınmaktır.
cjs

1
Oldukça yanılıyorsunuz, çünkü soruyu soran kişinin ihtiyaçlarını ve niyetlerini tamamen yanlış yorumluyorsunuz (veya görmezden geliyorsunuz). Soruyu, özellikle son iki noktayı dikkatlice tekrar okumanızı öneririm.
cjs

2

Değerli taşlar kullanmadan anahtar / değer komutları için basit bir komut satırı ayrıştırıcısı istiyorsanız:

Ancak bu yalnızca her zaman anahtar / değer çiftleriniz varsa işe yarar.

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

Herhangi bir kontrole ihtiyacınız yoksa, sadece şunları kullanabilirsiniz:

opts = {} 

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

2

Komut dosyalarımın çoğunun üstünde kullandığım kod pasajı:

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

Hızlı ve kirli komut dosyalarımda ek dosyalara ihtiyaç duymaktan da nefret ediyorum. Benim çözümüm, neredeyse istediğiniz şey. Komut dosyalarımın üst kısmına, komut satırını ayrıştıran ve konumsal değişkenleri yapıştıran ve bir Hash nesnesine (genellikle arghash adını verdiğim bir nesneye atanan) 10 satırlık bir kod parçası yapıştırıyorum. aşağıdaki örneklerde .

İşte ayrıştırmak isteyebileceğiniz örnek bir komut satırı ...

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

Hangisi böyle bir Hash olur.

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

Buna ek olarak, Hash'e iki kolaylık yöntemi eklenmiştir:

  • argc() anahtar olmayan bağımsız değişkenlerin sayısını döndürür.
  • switches() mevcut anahtarların anahtarlarını içeren bir dizi döndürür

Bu, bazı hızlı ve kirli şeylere izin vermek anlamına gelir ...

  • Geçilen anahtarlardan bağımsız olarak doğru sayıda konumsal bağımsız değişkene sahip olduğumu doğrulayın ( arghash.argc == 2 )
  • Konumsal argümanlardan önce görünen veya konumsal argümanlarla serpiştirilmiş anahtarlardan bağımsız olarak konumsal argümanlara göreceli konumlarına göre erişin (örneğin arghash[1]her zaman ikinci anahtar olmayan argümanı alır).
  • arghash['--max=']Örnek komut satırı verildiğinde '15' değerini veren "--max = 15" gibi komut satırında değer atanmış anahtarları destekleyin .
  • Komut satırında bir anahtarın varlığını veya yokluğunu test etmek için çok basit bir gösterim arghash['-s']kullanın; bu, mevcutsa doğru, yoksa sıfır olarak değerlendirilir.
  • Bir anahtarın veya anahtar alternatiflerinin varlığını test edin.

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

  • Aşağıdaki gibi ayarlı işlemleri kullanarak geçersiz anahtarların kullanımını tanımlayın

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

  • Eksik bağımsız değişkenler için varsayılan değerleri Hash.merge(), aşağıdaki örnekte olduğu gibi, biri ayarlanmadıysa -max = için bir değer dolduran ve geçirilmemişse 4. bir konumsal bağımsız değişken ekleyen bir basit kullanarak belirtin .

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


(Bunu kod biçimlendirmesini temizlemek için düzenledim, özellikle blok ve kontrol yapısını daha net hale getirmek için hizalamayı kullanarak, ki bu özellikle noktalama işaretleriyle bu kadar yoğun bir şeyde önemli olduğunu düşünüyorum. Ancak yeni biçimlendirmeden nefret ediyorsanız lütfen çekinmeyin düzenlemeyi geri almak için.)
cjs

Bu, dünyadaki okuması en kolay şey değilse de oldukça hoş. Aynı zamanda "sözdizimi" bağımsız değişkenini değiştirmenin (burada, konumsal bağımsız değişkenlerden sonra seçeneklere izin vermek ve kullanmak dışında seçenek argümanlarına izin vermemek =) ihtiyacınız olan kodda bir fark yaratabileceğini de göstermesini seviyorum .
cjs

Yeniden biçimlendirdiğiniz için teşekkürler. Okuması kesinlikle belirsizdir ve açıklık için kolayca kod uzunluğu değiş tokuş edilebilir. Artık bu koda güvendiğime göre, az ya da çok, ona bir mücevher gibi davranıyorum ve hiçbir zaman kapakların altında ne yaptığını anlamaya çalışmıyorum (bu yüzden artık güvendiğim için netlik önemli değil).
David Foster

1

Bu, kabul edilen cevaba çok benziyor, ancak basit ayrıştırıcımdaARGV.delete_if hangisini kullanıyorum . Tek gerçek fark, bağımsız değişkenli seçeneklerin birlikte olması gerektiğidir (örneğin ).-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

Görünüşe göre @WilliamMorgan ve ben benzer düşünüyorum. Dün gece Github'a yayınladım, şimdi gördüğüm şey, Github'da OptionParser için bir arama yaptıktan sonra Trollop'a benzer bir kitaplık (Nasıl adlandırıldı?), Bkz. Anahtarları

Birkaç farklılık var ama felsefe aynı. Belirgin bir fark, Switch'lerin OptionParser'a bağlı olmasıdır.


0

Gelişiyorum Beğeni denilen kendi opsiyon ayrıştırıcı mücevher .

Bunu, git tarzı komut satırı arayüzleri oluşturmak ve her komutun işlevselliğini ayrı sınıflara temiz bir şekilde ayırabilmek istediğim için yazdım, ancak tüm komut çerçevesi olmadan da kullanılabilir:

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

Henüz kararlı bir sürüm yok, ancak aşağıdaki gibi bazı özellikleri zaten uyguladım:

  • özel seçenek ayrıştırıcı
  • Hem minimum hem de isteğe bağlı olarak izin veren seçenek argümanlarının esnek ayrıştırılması
  • birçok seçenek stili için destek
  • aynı seçeneğin birden çok örneğini değiştirme, ekleme veya yükseltme
  • özel seçenek işleyicileri
  • özel tür işleyiciler
  • ortak standart kitaplık sınıfları için önceden tanımlanmış işleyiciler

Komutlara çok fazla vurgu yapıldığından, basit komut satırı ayrıştırması için biraz ağır olabilir, ancak iyi çalışıyor ve bunu tüm projelerimde kullanıyorum. Komut arayüzü yönüyle ilgileniyorsanız, daha fazla bilgi ve örnekler için projenin GitHub sayfasına bakın.


1
Acclaim'i şiddetle tavsiye ederim. Kullanımı kolaydır ve ihtiyacınız olan tüm seçeneklere sahiptir.
bowsersenior

0

Bir komutun en fazla bir eylemi ve aşağıdaki gibi keyfi sayıda seçeneği olduğunu varsayalım:

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

Doğrulama olmadan ayrıştırma şu şekilde olabilir:

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'da), harici seçenek ayrıştırıcısına bağımlılık yok. İşi bitirir. Muhtemelen diğerleri kadar tam özellikli değil, ancak 46LOC.

Kodu kontrol ederseniz, temeldeki tekniği oldukça kolay bir şekilde çoğaltabilirsiniz - lambdalar atayın ve gerçekten harici bir kitaplık istemiyorsanız bayrağı uygun sayıda argüman sağlamak için arity'yi kullanın.

Basit. Ucuz.


DÜZENLEME : makul bir komut satırı ayrıştırıcısı yapmak için onu bir komut dosyasına kopyalayıp / yapıştırabileceğinizi düşündüğüm için temeldeki kavram kaynatıldı. Kesinlikle hafızaya alacağım bir şey değil , ancak lambda arity'yi ucuz bir ayrıştırıcı olarak kullanmak yeni bir fikir:

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

Lütfen sorunun sonundaki 1. noktayı okuyun. Yazdığınız yapamıyorsanız tüm buradan cevap gerekli olan kod hakkı, bu soruya bir cevap değil.
cjs

İyi bir nokta! Sanırım o zamanlar kütüphanenin yeterince küçük olduğunu varsayarak, harici bir bağımlılığa ihtiyaç duymadan üzerinde çalıştığınız betiğe her şeyi kopyalayıp / yapıştırabilirsiniz, ama kesinlikle hafızaya vereceğim temiz bir tek satırlık değil 2. amacınızı yerine getirmek için. Temel kavramın yeni ve yeterince havalı olduğunu düşünüyorum ve sorunuzu biraz daha uygun şekilde yanıtlayan kaynatılmış bir versiyon hazırladım.
Ben Alavi

-1

Bir süredir üzerinde çalıştığım kendi basit seçenek ayrıştırıcımı paylaşacağım. Yalnızca 74 satırlık bir koddur ve Git'in dahili seçenek ayrıştırıcısının yaptığı şeyin temellerini yapar. OptionParser'ı ve ayrıca Git'i ilham olarak aldım.

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

Şöyle görünüyor:

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

Kodu kontrol etmedin bile. Ayrıştırma kodunu çıkararak başka bir cevap verdim.
FelipeC

74 satır olduğunu söylemene gerek yoktu. Ancak, şimdi baktım ve hala gereksinim 2'nin ilk cümlesini ihlal ediyor. (Bu yanıt, site dışı bir bağlantı vermek yerine kodu yanıtınıza dahil etmeniz gereken Yığın Taşma kuralını da ihlal ediyor.)
cjs

-1

EasyOptions , herhangi bir seçenek ayrıştırma kodu gerektirmez. Sadece yardım metnini yazın, gerekli, bitti.

## 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, gerekli ifadeler içermeyen tek bir Ruby dosyasıdır ve hatırlanması gereken hiçbir ayrıştırma kodu yoktur. Bunun yerine, yeterince güçlü ancak hatırlaması kolay bir şey istiyorsunuz.
Renato Silva
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.