Насправді дешевий варіант командного рядка проходить в Ruby


114

EDIT: Будь ласка, будь ласка , будь ласка , прочитайте два вимоги , перелічені в нижній частині цієї посади , перш ніж відповісти. Люди продовжують публікувати свої нові дорогоцінні камені та бібліотеки та інше, що явно не відповідає вимогам.

Іноді мені хочеться дуже дешево зламати деякі параметри командного рядка в простий сценарій. Веселий спосіб зробити це, не маючи справи з геттоптами чи розбором чи іншим подібним:

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

Це не зовсім звичайний синтаксис параметрів Unix, оскільки він буде приймати параметри командного рядка, які не є опціями, як у " myprog -i foo bar -q", але я можу з цим жити. (Деякі люди, такі як розробники Subversion, вважають за краще це. Іноді я теж роблю це.)

Варіант, який є лише наявним або відсутній, не може бути реалізований набагато просто, ніж вище. (Одне призначення, один виклик функції, один побічний ефект.) Чи існує однаково простий спосіб розібратися з параметрами, які приймають параметр, наприклад " -f ім'я файлу "?

Редагувати:

Один момент я не робив раніше, тому що мені не стало зрозуміло, поки автор Trollop не згадав, що бібліотека вміщується "в одному файлі [800 рядків]" - це те, що я шукаю не лише чистого синтаксису, але для техніки, яка має такі характеристики:

  1. Цілість коду можна включити до файлу сценарію (не перевантажуючи власне сам сценарій, який може бути лише пару десятків рядків), так що можна скинути один файл у bindir на будь-якій системі зі стандартним Ruby 1.8 . [5-7] встановлення та використання його. Якщо ви не можете написати сценарій Ruby, який не вимагає операторів, і де код для розбору кількох варіантів знаходиться під десяток рядків, ви не виконаєте цю вимогу.

  2. Код невеликий і простий, що можна запам'ятати його достатньо, щоб безпосередньо набрати код, який буде робити хитрість, а не вирізати та вставляти з іншого місця. Подумайте про ситуацію, коли ви знаходитесь на консолі розстріляного каналу, що не має доступу до Інтернету, і ви хочете скласти швидкий сценарій для використання клієнтом. Я не знаю про вас, але (окрім невиконання вимоги, наведеної вище) запам'ятовування навіть 45 рядків спрощеного мікрооптипарту - це не те, що я хочу зробити.


2
Просто цікаво заперечення проти getoptlong?
Марк Кері

Багатослівність його. У getoptlog іноді код розбору параметрів довший, ніж та частина сценарію, яка насправді виконує роботу. Це не просто естетичне питання, а питання вартості обслуговування.
cjs

8
Я не розумію вимоги щодо включення сценарію - і обидва, getoptlongі optparseзнаходяться в стандартній бібліотеці ruby, тому вам НЕ ПОТРІБНО копіювати їх під час розгортання вашого сценарію - якщо рубін працює на цій машині, тоді require 'optparse'і require 'getoptlong'буде працювати теж.
чемпіон

Дивіться stackoverflow.com/questions/21357953/… , а також відповідь Вільяма Моргана нижче про Trollop.
безстрашний_фол

@CurtSampson Не можу повірити, скільки людей не відповіли на ваше запитання. Так чи інакше, нарешті отримав хорошу відповідь про 3 повідомлення вниз XD XD
OneChillDude

Відповіді:


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. Ви можете робити все, що завгодно. І ви отримуєте прекрасну довідкову сторінку, відформатовану відповідно до ширини екрана, автоматичні короткі імена аргументів, перевірку типу ... все, що вам потрібно.

Це один файл, тому ви можете опустити його у свою lib / каталог, якщо не хочете формальної залежності. Він має мінімальний DSL, який легко підібрати.

LOC на вибір людей. Це важливо.


39
BTW, +1 за те, що написав Trollop (про який вже згадувалося тут), але не соромтесь трохи звузити перший абзац.
cjs

33
Він має право скаржитися в цій справі, я боюся. Якщо ви подивитесь на альтернативи: [1 ] [2 ] [3 ], адже в основному це просто обробка простого рядкового масиву (ні, насправді, нехай це занурюється), ви не можете не задатися питанням ЧОМУ? Що ви отримуєте від усього цього набряку? Це не C, де рядки є "проблематичними". Звичайно, у кожного своє. :)
srcspider

50
Будь ласка, не звужуйте це трохи. Це праведна стяжка, брате.
Вільям Піетрі

7
Не соромтесь трохи зменшити тон десятого слова.
Ендрю Грімм

3
+1 для Trollop. Я використовую його для моєї системи автоматизації тестів, і вона просто працює. Плюс це так просто кодувати з цим, що іноді я переставляю свій банер просто для того, щоб відчути радість від нього.
кінофрост

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
Дякую, я не можу зрозуміти, як це не відповідає запиту ОП, особливо враховуючи його все в стандартній lib, порівняно з необхідністю встановлення / продажу будь-якого нестандартного коду
dolzenko

3
це схоже на версію trollop, за винятком того, що не потрібен додатковий файл.
Клавдіу

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
Відповідає на питання, але людині, Троллопу, здається, набагато простіше впоратися. Навіщо винаходити колесо, коли попередньо виготовлене колесо настільки гладке?
Mikey TK

7
Заздалегідь виготовлене колесо не плавніше Прочитайте питання ще раз уважно, приділяючи ретельну увагу вимогам.
cjs

2
+1 Іноді вам потрібно винайти колесо, тому що ви не хочете або просто не можете використовувати інші залежності, наприклад Trollop.
lzap

Trollop не потрібно встановлювати як дорогоцінний камінь. Ви можете просто libзапустити один файл у папку чи код і використовувати його, навіть не торкаючись рубігем.
Овербрид

Для мене, мені довелося змінити , when /^-/ then usage("Unknown option: #{ARGV[0].inspect}")щоб when /^-/ then usage("Unknown option: #{ARGV.shift.inspect}")або було б отримати в нескінченний цикл використання
Casey

36

Так як ніхто не з'явився згадувати про це, і назва це відносяться до дешевої командного рядка синтаксичного аналізу, чому б просто не дозволити рубін перекладач зробити роботу для вас? Якщо ви перейдете до -sкомутатора (наприклад, у своєму шебангу), ви отримаєте безперешкодні комутатори безкоштовно, призначені для однобуквених глобальних змінних. Ось ваш приклад використання цього перемикача:

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

І ось результат, коли я зберігаю це як ./testі 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"]

Детальніше ruby -hдив.

Це повинно бути таким же дешевим, як це виходить. Якщо ви спробуєте комутатор -:, він підніме ім'я NameError , тому там є деяка перевірка. Звичайно, після аргументу невмикання ви не можете мати комутаторів, але якщо вам потрібно щось фантазійне, ви дійсно повинні користуватися мінімальним OptionParser. Насправді, єдине, що мене дратує у цій техніці, - це те, що ви отримаєте попередження (якщо ви їх включили) під час доступу до невстановленої глобальної змінної, але це все-таки фальси, тому це працює чудово для викидання інструментів та швидкої роботи сценарії.

Одне застереження, на яке вказував FelipeC в коментарі до " Як зробити дійсно дешевий розбір варіантів командного рядка в Ruby ", - це те, що ваша оболонка може не підтримувати 3-токеновий шебанг; Вам може знадобитися замінити /usr/bin/env ruby -wфактичний шлях до вашого рубіну (як /usr/local/bin/ruby -w), або запустити його із скрипту для обгортки, або чогось іншого.


2
Дякую :) Я впевнений, що останні два роки він не чекав цієї відповіді.
DarkHeart

3
Я справді чекав цієї відповіді останні два роки. :-) Більш серйозно, це такий розумний погляд, який я шукав. Попереджувальна річ трохи дратує, але я можу придумати способи її пом'якшення.
cjs

Радий, що можу (врешті-решт) допомогти, @CurtSampson, прапори МРТ вирвані прямо з Perl, де вони, як правило, безоплатно використовуються в оболонках з оболонками. Не соромтеся приймати, якщо відповідь все ще корисна для вас. :)
bjjb

1
Ви не можете використовувати декілька аргументів у shebang в Linux. / usr / bin / env: 'ruby -s': Немає такого файлу чи каталогу
FelipeC

13

Я створив мікрооптипар для того, щоб заповнити цю очевидну потребу в короткому, але простому у використанні варіанті аналізатора. Він має синтаксис, подібний до 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, оскільки ви залежите від цього і вимагаєте, optparseщо це (дайте або візьміть) 1937 рядків.
Телемах

6
Порівнювати кількість ліній абсолютно нормально, оскільки optparseце бібліотека за замовчуванням, тобто вона постачається з кожною установкою рубіну. Trollopце стороння бібліотека, отже, ви повинні імпортувати повний код кожного разу, коли ви хочете включити його в проект. µ-optparse завжди потребує лише ~ 70 рядків, оскільки optparseвін вже є.
Флоріан Пільц

8

Я повністю розумію, чому ви хочете уникати optparse - це може отримати занадто багато. Але є кілька далеко «легших» рішень (порівняно з OptParse), які надходять як бібліотеки, але досить прості, щоб зробити єдину установку дорогоцінного каміння вартістю.

Наприклад, ознайомтеся з цим прикладом 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, а потім бар. Це зручно для викидання сценаріїв.


3

Ви можете спробувати щось на кшталт:

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

3

Ви розглядали Тор за вікатами? Я думаю, це набагато чистіше, ніж optparse. Якщо у вас вже написаний сценарій, можливо, буде відформатувати його або переробити його на Thor, але це робить параметри обробки дуже простими.

Ось приклад фрагменту з 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

Тор автоматично відображає команди як такі:

app install myname --force

Це перетворюється на:

MyApp.new.install("myname")
# with {'force' => true} as options hash
  1. Наслідуйте Thor, щоб перетворити клас у перетворювач опцій
  2. Відображення додаткових недійсних ідентифікаторів для конкретних методів. У цьому випадку перетворіть -L у: список
  3. Опишіть метод одразу нижче. Перший параметр - це інформація про використання, а другий - опис.
  4. Надайте будь-які додаткові варіанти. Вони будуть переміщені з парафій. У цьому випадку додаються опції --force та -f.

Мені подобається річ із картографічним відображенням, оскільки один двійковий файл із купою підкоманд - це те, що я часто роблю. Все-таки, хоч ти пройшов шлях від "світла". Чи можете ви знайти ще простіший спосіб виразити ту саму функціональність? Що робити, якщо вам не потрібно було друкувати --helpвихід? Що робити, якщо "head myprogram.rb" виявився довідкою?
cjs

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

Щоправда, це бібліотека. Однак, при <800 LOC, це досить мізерно. gitorious.org/trollop/mainline/blobs/master/lib/trollop.rb
g33kz0r

1
Я якось думав, що, можливо, 30-50 рядків буде добре, якби я зайшов так далеко, щоб використовувати "бібліотеку". Але знову ж таки, я думаю, що коли ви дістанетесь до окремого файлу з кодом, дизайн API важливіший, ніж кількість рядків. І все-таки я не впевнений, що хотів би включити його в одноразовий сценарій, який я просто хочу вставити в каталог бін у випадковій системі.
cjs

1
Ви отримали це назад: справа у тому, щоб уникнути необхідності складати складнішу стратегію розгортання.
cjs

1
Ви абсолютно помиляєтесь, оскільки ви повністю неправильно трактуєте (або ігноруєте) потреби та наміри людини, яка поставила запитання. Я пропоную вам перечитати питання уважно, особливо останні два пункти.
cjs

2

Якщо ви хочете простий аналізатор командних рядків для команд ключ / значення без використання дорогоцінних каменів:

Але це працює лише в тому випадку, якщо у вас завжди є пара ключів / значень.

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

Я також ненавиджу вимагати отримання додаткових файлів у своїх швидких та брудних сценаріях. Моє рішення дуже майже те, що ви просите. Я вставляю фрагмент коду в 10 рядків у верхній частині будь-якого зі моїх сценаріїв, який аналізує командний рядок і вставляє позиційні аргументи та перемикається на об’єкт Hash (зазвичай призначається об'єкту, який я назвав arghash у прикладах нижче).

Ось приклад командного рядка, який ви можете проаналізувати ...

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

Який би став такий хеш.

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

На додаток до цього в хеш додаються два зручні методи:

  • argc() поверне кількість аргументів без комутаторів.
  • switches() поверне масив, що містить клавіші для присутніх комутаторів

Це означає, щоб дозволити деякі швидкі та брудні речі, наприклад ...

  • Перевірте, я отримав потрібну кількість позиційних аргументів незалежно від комутаторів, переданих у ( arghash.argc == 2)
  • Доступ до позиційних аргументів за їх відносним положенням, незалежно від перемикачів, що з'являються перед або перемежовуються з позиційними аргументами (наприклад, arghash[1]завжди отримує другий аргумент, що не перемикається).
  • Підтримка присвоєних значенням перемикачів у командному рядку, таких як "--max = 15", до якого можна отримати доступ, завдяки arghash['--max=']якому отримується значення "15", наведене в прикладі командного рядка.
  • Перевірте наявність чи відсутність комутатора в командному рядку, використовуючи дуже просту нотацію, таку, arghash['-s']яка оцінюється як істинна, якщо вона присутня, та нуль, якщо її немає.
  • Перевірте наявність вимикача або альтернативи вимикачів, використовуючи задані операції типу

    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

Це досить приємно, якщо не найлегше читати. Мені подобається, що він також демонструє, що зміна аргументу "синтаксис" (тут, дозволити параметри після позиційних аргументів і заборонити аргументи параметрів, крім використання =), може змінити необхідний вам код.
cjs

Дякую за переформатування. Це, безумовно, незрозуміле для читання, і можна легко торгувати довжиною коду для наочності. Тепер, коли я довіряю цьому коду, більш-менш я ставлюсь до нього, як до дорогоцінного каміння, і ніколи не намагаюся зрозуміти, що він робить під кришками (тому ясність вже не важлива, коли я маю довіру).
Девід Фостер

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, що я бачу - це схожа бібліотека на Trollop (названа як?) Після того, як я здійснив пошук OptionParser в Github, див. Перемикачі

Існує кілька відмінностей, але філософія однакова. Одне очевидною відмінністю є те, що комутатори залежать від OptionParser.


0

Я розробляю власний дорогоцінний камінь для розбору варіантів під назвою Acclaim .

Я написав це, тому що хотів створити інтерфейси командного рядка в стилі git і мати можливість чітко розділити функціональність кожної команди на окремі класи, але це також можна використовувати без усієї команди команд:

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

Стабільного випуску поки немає, але я вже реалізував такі функції, як:

  • спеціальний аналізатор варіантів
  • гнучкий аналіз аргументів опції, що дозволяє робити як мінімальну, так і необов'язкову
  • підтримка багатьох стилів опцій
  • замінити, додати або підняти в декількох екземплярах одного і того ж параметра
  • користувальницькі опціони
  • користувальницькі типи обробників
  • заздалегідь визначені обробники для загальних стандартних бібліотечних класів

Великий акцент робиться на команди, тому це може бути трохи важким для простого розбору командного рядка, але він працює добре, і я використовую його у всіх своїх проектах. Якщо вас цікавить аспект командного інтерфейсу, ознайомтеся зі сторінкою проекту GitHub для отримання додаткової інформації та прикладів.


1
Я настійно рекомендую Висловити позицію. Він простий у використанні та має всі необхідні варіанти.
bowsersenior

0

Припустимо, команда має максимум одну дію та довільну кількість таких варіантів:

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.

Якщо ви перевіряєте код, ви можете досить просто скопіювати основну техніку - призначте лямбда і використовуйте arity, щоб забезпечити правильну кількість аргументів, які слідкують за прапором, якщо ви дійсно не хочете зовнішньої бібліотеки.

Простий. Дешево.


EDIT : основна концепція впала, оскільки я думаю, ви можете скопіювати / вставити її в сценарій, щоб зробити розумний аналізатор командного рядка. Це, безумовно, не те, що я б взяв на себе пам'ять, але використання лямбда-аріті як дешевого аналізатора - це нова ідея:

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

Гарна думка! Я думаю, що в той час, коли я вважав, що ліб досить малий, щоб ви могли скопіювати / вставити всю річ у будь-який сценарій, над яким ви працювали, не потребуючи зовнішньої залежності, але це, безумовно, не чистий однолінійний лайнер, який я б взяв на пам'ять щоб виконати свою точку №2. Я думаю, що основна концепція є достатньо новою і крутою, що я пішов уперед і створив розкидану версію, яка трохи відповідніше відповідає на ваше запитання.
Бен Алаві

-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

Мені не потрібно було враховувати, що ви сказали, що це 74 рядки. Однак, я щойно переглянув це, і він все ще порушує перше речення вимоги 2. (Ця відповідь також порушує конвенцію про переповнення стека, що ви повинні включати код у свою відповідь, а не надавати посилання поза сайтом.)
cjs

-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 - це один файл Ruby без необхідних операторів, і немає коду для розбору, який би запам'ятовувався. Здається, ви хочете щось вбудоване, що є досить потужним, але простим для запам'ятовування.
Ренато Сільва
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.