Ruby에서 매우 저렴한 명령 줄 옵션 구문 분석


114

편집 : 제발, 제발 , 제발 회신하기 전에이 게시물의 하단에 나와있는 두 가지 요구 사항을 참조하십시오. 사람들은 요구 사항을 분명히 충족하지 못하는 새로운 보석과 라이브러리 등을 계속 게시합니다.

때로는 명령 줄 옵션을 간단한 스크립트로 매우 저렴하게 해킹하고 싶습니다. 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 개발자와 같은 일부 사람들은 이것을 선호합니다. 때로는 저도 그렇습니다.)

그냥 존재하거나없는 옵션은 위의 것보다 훨씬 간단하게 구현할 수 없습니다. (하나의 할당, 하나의 함수 호출, 하나의 부작용) " -f filename " 과 같이 매개 변수를 사용하는 옵션을 처리하는 똑같이 간단한 방법이 있습니까?

편집하다:

Trollop의 저자가 라이브러리가 "하나의 [800 줄] 파일에 적합"하다고 언급 할 때까지 명확하지 않았기 때문에 이전에 언급하지 않은 한 가지 요점은 내가 깨끗한 것만을 찾고 있다는 것입니다. 구문이지만 다음과 같은 특성을 가진 기술에 사용됩니다.

  1. 코드 전체를 스크립트 파일에 포함 할 수 있으므로 (실제 스크립트 자체를 압도하지 않고 몇 줄에 불과할 수 있음) bin표준 Ruby 1.8을 사용하는 모든 시스템 의 디렉터리에 단일 파일을 드롭 할 수 있습니다. . [5-7] 설치 및 사용. require 문이없는 Ruby 스크립트를 작성할 수없고 몇 가지 옵션을 구문 분석하는 코드가 12 줄 정도 미만인 경우이 요구 사항이 실패합니다.

  2. 코드는 작고 단순해서 다른 곳에서 잘라내어 붙여 넣는 대신 트릭을 수행 할 코드를 직접 입력 할 수있을만큼 충분히 기억할 수 있습니다. 인터넷에 액세스 할 수없는 방화벽 서버의 콘솔에있는 상황을 생각해보십시오. 클라이언트가 사용할 수있는 빠른 스크립트를 함께 사용하려고합니다. 나는 당신에 대해 잘 모르지만 (위의 요구 사항에 실패하는 것 외에도) 45 줄의 단순화 된 마이크로 옵 파스조차도 암기하는 것은 내가 신경 쓰는 일이 아닙니다.


2
getoptlong에 대한 이의가 궁금 하신가요?
Mark Carey

그것의 장황함. getoptlog를 사용하면 코드를 구문 분석하는 옵션이 실제로 작업을 수행하는 스크립트 부분보다 긴 경우가 있습니다. 이것은 미적 문제가 아니라 유지 보수 비용 문제입니다.
cjs

8
모두 - 나는 스크립트 포함 요구 사항을 이해하지 못하는 getoptlongoptparse루비 후, 해당 시스템에서 작동하는 경우 - 당신은 당신의 스크립트를 배포 할 때 복사 할 필요가 없습니다, 표준 루비 라이브러리에있는 require 'optparse'또는 require 'getoptlong'너무 일 것이다.
rampion

참조 stackoverflow.com/questions/21357953/... 뿐만 아니라 매춘부에 대한 아래의 윌리엄 모건의 답변을.
fearless_fool

@CurtSampson 얼마나 많은 사람들이 귀하의 질문에 대답하지 않았는지 믿을 수 없습니다. 어느 쪽이든, 마지막 XD XD 아래 3 개 게시물에 대한 좋은 대답을 얻었다
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 09-06-19

33
그는이 경우 불만을 제기 할 권리가 있습니다. 대안을 볼 때 : [1 ] [2 ] [3 ], 기본적으로 단순한 문자열 배열을 처리하는 것이 무엇인지 (실제로는 그렇게하지 마십시오) 왜 그런지 궁금해 할 수 없습니다. 그 모든 팽창에서 무엇을 얻습니까? 이것은 문자열이 "문제가있는"C가 아닙니다. 물론 각자 자신에게. :)
srcspider 2010-08-22

50
이걸 조금 낮추지 마세요. 의로운 스크 리드 야, 형제.
William Pietri

7
열 번째 단어를 조금 낮추십시오.
Andrew Grimm 2011 년

3
Trollop에 +1. 테스트 자동화 시스템에 사용하고 Just Works입니다. 게다가 코드 작성이 너무 쉬워서 가끔 배너의 즐거움을 경험하기 위해 배너를 재정렬합니다.
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
덕분에,이, 특히 설치 / 비 표준 코드 vendoring의 필요에 비해, 모든 표준 lib에 자사을 고려하여 OPS 요청 맞지 않는 볼 수 없습니다
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
질문에 답하지만, 트롤 롭은 처리하기가 훨씬 더 쉬운 것 같습니다. 사전 제작 된 휠이 훨씬 더 부드러울 때 휠을 재발 명하는 이유는 무엇입니까?
Mikey TK

7
미리 만들어진 휠이 더 부드럽 지 않습니다. 요구 사항에주의하면서 질문을주의 깊게 다시 읽으십시오.
CJS

2
+1 때때로 당신 은 트롤 롭과 같은 다른 의존성을 원하지 않거나 단순히 사용할 수 없기 때문에 바퀴를 재발 명해야합니다 .
lzap 2014

Trollop은 gem으로 설치할 필요가 없습니다. lib폴더 나 코드 에 파일 하나를 놓아두면 rubygems를 건드리지 않고도 사용할 수 있습니다.
Overbryd

나를 위해, 나는 변경했다 when /^-/ then usage("Unknown option: #{ARGV[0].inspect}")으로 when /^-/ then usage("Unknown option: #{ARGV.shift.inspect}")또는 무한 사용 루프에 들어갈 것
케이시

36

아무도 그것을 언급 등장하지 않으며, 제목이 때문에 않습니다 를 참조 저렴 명령 줄 구문 분석, 왜 그냥 당신을 위해 일을 통역 루비를 보자? -s스위치 를 전달하면 (예 : shebang에서) 단일 문자 전역 변수에 할당 된 더럽고 간단한 스위치가 무료로 제공됩니다. 이 스위치를 사용한 예는 다음과 같습니다.

#!/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가 발생 -:하므로 거기에 몇 가지 유효성 검사가 있습니다. 물론, non-switch 인수 후에는 어떤 스위치도 가질 수 없지만, 뭔가 멋진 것이 필요하다면, 최소한 OptionParser를 사용해야합니다. 사실,이 기술에 대해 저를 짜증나게하는 유일한 점은 설정되지 않은 전역 변수에 액세스 할 때 경고 (활성화 한 경우)가 표시되지만 여전히 거짓이므로 일회용 도구 및 빠른 스크립트.

" Ruby에서 정말 저렴한 명령 줄 옵션 구문 분석을 수행하는 방법 "의 주석에서 FelipeC가 지적한 한 가지주의 사항은 쉘이 3- 토큰 shebang을 지원하지 않을 수 있다는 것입니다. /usr/bin/env ruby -w루비의 실제 경로 (예 :) 로 바꾸 /usr/local/bin/ruby -w거나 래퍼 스크립트 등에서 실행해야 할 수 있습니다.


2
감사합니다 :) 그가 지난 2 년 동안이 답변을 기다리지 않았기를 바랍니다.
DarkHeart 2013

3
나는 정말로 지난 2 년 동안이 대답을 기다리고 있었다. :-) 더 진지하게, 이것은 내가 찾던 일종의 영리한 생각입니다. 경고는 약간 성가 시지만이를 완화 할 방법을 생각할 수 있습니다.
CJS

@CurtSampson, @CurtSampson, MRI의 깃발은 Perl에서 곧바로 찢어지며 쉘 한 줄에서 무상으로 사용되는 경향이 있습니다. 답변이 여전히 도움이된다면 받아 들일 수 있습니다. :)
bjjb 2014

1
Linux에서는 shebang에서 여러 인수를 사용할 수 없습니다. / 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

입력이 기본값과 동일한 유형인지 확인하고, 짧고 긴 접근자를 생성하고, 유효하지 않은 인수가 주어지면 설명 오류 메시지를 인쇄합니다.

내가 가진 문제에 대해 각 옵션 파서 를 사용하여 여러 옵션 파서비교했습니다 . 이 예와 요약을 사용하여 유익한 결정을 내릴 수 있습니다. 목록에 더 많은 구현을 자유롭게 추가하십시오. :)


라이브러리 자체가 훌륭 할 것 같습니다. 그러나 optparse1937 줄에 의존하고 요구하기 때문에 줄 수를 Trollop과 비교하는 것이 불명예하지 않습니까 ?
Telemachus

6
줄 수를 비교 optparse하는 것은 기본 라이브러리 이기 때문에 절대적으로 괜찮습니다 . 즉, 모든 루비 설치와 함께 제공됩니다. Trollop타사 라이브러리이므로 프로젝트에 포함 할 때마다 전체 코드를 가져와야합니다. µ-optparse optparse는 이미 존재 하므로 항상 ~ 70 줄만 필요합니다 .
Florian Pilz

8

optparse를 피하고 싶은 이유를 완전히 이해합니다. 너무 많이 얻을 수 있습니다. 그러나 라이브러리로 제공되지만 단일 gem 설치를 가치있게 만들 수있을만큼 간단한 몇 가지 훨씬 "가벼운"솔루션 (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를 호출 한 다음 bar를 호출합니다. 일회용 스크립트에 편리합니다.


3

다음과 같이 시도 할 수 있습니다.

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

3

wycats의 Thor 를 고려해 보셨습니까? 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

Thor는 다음과 같은 명령을 자동으로 매핑합니다.

app install myname --force

다음으로 변환됩니다.

MyApp.new.install("myname")
# with {'force' => true} as options hash
  1. Thor에서 상속하여 클래스를 옵션 매퍼로 전환
  2. 유효하지 않은 추가 식별자를 특정 메서드에 매핑합니다. 이 경우 -L을 : list로 변환하십시오.
  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 디자인이 줄 수보다 더 중요하다고 생각합니다. 그래도 무작위 시스템의 bin 디렉토리에 넣고 싶은 일회성 스크립트에 포함시키고 싶지는 않습니다.
cjs 09-06-19

1
거꾸로 이해했습니다. 요점은 더 복잡한 배포 전략을 사용하지 않는 것입니다.
cjs

1
질문을 한 사람의 필요와 의도를 완전히 오해 (또는 무시)하고 있기 때문에 당신은 아주 틀 렸습니다. 질문, 특히 마지막 두 가지 사항을주의 깊게 다시 읽어 보시기 바랍니다.
cjs

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

또한 빠르고 더러운 스크립트에 추가 파일이 필요한 것도 싫어합니다. 내 솔루션은 당신이 요구하는 것입니다. 명령 줄을 구문 분석하고 위치 인수를 고정하고 Hash 개체로 전환하는 스크립트의 맨 위에 10 줄의 코드 스 니펫을 붙여 넣습니다 (일반적으로 내가 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']경우 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 = 값이 설정되지 않은 경우 값을 채우고 전달되지 않은 경우 네 번째 위치 인수를 추가하는 아래 예제와 같은 간단한 예를 사용하여 누락 된 인수에 대한 기본값을 지정 하십시오.

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


(주로 정렬을 사용하여 블록 및 제어 구조를 더 명확하게 만드는 코드 서식을 깔끔하게 개선하기 위해 이것을 편집했습니다. 특히 구두점이 이처럼 밀집된 부분에서 중요하다고 생각합니다.하지만 새 서식이 싫다면 부담없이 사용해주세요. 편집을 취소합니다.)
cjs jul

이것은 세상에서 가장 읽기 쉬운 것은 아니지만 꽤 좋습니다. 또한 인수 "구문"을 변경하면 (여기서는 위치 인수 다음에 옵션을 허용하고를 사용하는 경우를 제외하고 옵션 인수를 허용하지 않음 =) 필요한 코드에 차이를 만들 수 있음을 보여줍니다.
cjs

다시 포맷 해 주셔서 감사합니다. 읽기는 확실히 모호하며 명확성을 위해 코드 길이를 쉽게 바꿀 수 있습니다. 이제 저는이 코드를 어느 정도 신뢰하므로 보석처럼 취급하고 그것이 무엇을하고 있는지 알아 내려고하지 않습니다 (이제 신뢰를 얻었으므로 명확성은 더 이상 중요하지 않습니다).
David Foster

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에서 OptionParser를 검색 한 후 어제 밤에 Github에 릴리스했습니다. 이제 Trollop과 유사한 라이브러리 (이름은?)입니다. Switches를

몇 가지 차이점이 있지만 철학은 동일합니다. 한 가지 분명한 차이점은 스위치가 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
Acclaim을 강력히 추천합니다. 사용하기 쉽고 필요한 모든 옵션이 있습니다.
bowsersenior jul.

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를 ​​사용합니다.

단순한. 싼.


편집 : 합리적인 명령 줄 구문 분석기를 만들기 위해 스크립트에 복사 / 붙여 넣기 할 수 있다고 가정 할 때 기본 개념이 요약되었습니다. 그것은 확실히입니다 하지 싼 파서가 새로운 생각으로 내가 메모리에 커밋 것이 뭔가,하지만 람다 인수에 대응을 사용하여 :

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

좋은 지적! 나는 그 당시 lib가 외부 의존성이 필요하지 않고 작업하고 있던 스크립트에 전체를 복사 / 붙여 넣기 할 수있을만큼 충분히 작다고 생각했지만, 확실히 기억에 남을 깔끔한 한 줄은 아닙니다. 당신의 포인트 # 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

나는 당신이 그것이 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는 require 문이없는 단일 Ruby 파일이며 기억할 구문 분석 코드가 전혀 없습니다. 대신에 충분히 강력하면서도 기억하기 쉬운 삽입 가능한 것을 원하는 것 같습니다.
Renato Silva
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.