it-swarm.com.de

Analysieren Sie Befehlszeilenargumente in einem Ruby-Skript

Ich möchte ein Ruby-Skript von der Befehlszeile aus aufrufen und Parameter übergeben, die Schlüssel/Wert-Paare sind.

Befehlszeilenaufruf:

$ Ruby my_script.rb --first_name=donald --last_name=knuth

my_script.rb:

puts args.first_name + args.last_name

Was ist der Standard-Ruby-Weg, um dies zu tun? In anderen Sprachen muss ich normalerweise einen Options-Parser verwenden. In Ruby habe ich gesehen, dass wir ARGF.read haben, aber das scheint keine Schlüssel/Wert-Paare wie in diesem Beispiel zu funktionieren.

OptionParser sieht vielversprechend aus, aber ich kann nicht sagen, ob er diesen Fall tatsächlich unterstützt.

40
Don P

Basierend auf der Antwort von @MartinCortez, hier ein kurzes einmaliges Ereignis, das einen Hash aus Schlüssel/Wert-Paaren bildet, wobei die Werte mit einem =-Zeichen verbunden werden müssen. Es unterstützt auch Flag-Argumente ohne Werte:

args = Hash[ ARGV.join(' ').scan(/--?([^=\s]+)(?:=(\S+))?/) ]

…oder alternativ…

args = Hash[ ARGV.flat_map{|s| s.scan(/--?([^=\s]+)(?:=(\S+))?/) } ]

Wird mit -x=foo -h --jim=jam aufgerufen, wird {"x"=>"foo", "h"=>nil, "jim"=>"jam"} zurückgegeben, damit Sie Folgendes ausführen können:

puts args['jim'] if args.key?('h')
#=> jam

Zwar gibt es mehrere Bibliotheken, um dies zu handhaben - einschließlich GetoptLong, das in Ruby enthalten ist. Ich persönlich bevorzuge es jedoch, meine eigenen zu verwenden. Hier ist das Muster, das ich verwende, wodurch es einigermaßen generisch ist, nicht an ein bestimmtes Verwendungsformat gebunden und flexibel genug ist, um Flags, Optionen und erforderliche Argumente in verschiedenen Reihenfolge miteinander zu vermischen:

USAGE = <<ENDUSAGE
Usage:
   docubot [-h] [-v] [create [-s Shell] [-f]] directory [-w writer] [-o output_file] [-n] [-l log_file]
ENDUSAGE

HELP = <<ENDHELP
   -h, --help       Show this help.
   -v, --version    Show the version number (#{DocuBot::VERSION}).
   create           Create a starter directory filled with example files;
                    also copies the template for easy modification, if desired.
   -s, --Shell      The Shell to copy from.
                    Available shells: #{DocuBot::SHELLS.join(', ')}
   -f, --force      Force create over an existing directory,
                    deleting any existing files.
   -w, --writer     The output type to create [Defaults to 'chm']
                    Available writers: #{DocuBot::Writer::INSTALLED_WRITERS.join(', ')}
   -o, --output     The file or folder (depending on the writer) to create.
                    [Default value depends on the writer chosen.]
   -n, --nopreview  Disable automatic preview of .chm.
   -l, --logfile    Specify the filename to log to.

ENDHELP

ARGS = { :Shell=>'default', :writer=>'chm' } # Setting default values
UNFLAGGED_ARGS = [ :directory ]              # Bare arguments (no flag)
next_arg = UNFLAGGED_ARGS.first
ARGV.each do |arg|
  case arg
    when '-h','--help'      then ARGS[:help]      = true
    when 'create'           then ARGS[:create]    = true
    when '-f','--force'     then ARGS[:force]     = true
    when '-n','--nopreview' then ARGS[:nopreview] = true
    when '-v','--version'   then ARGS[:version]   = true
    when '-s','--Shell'     then next_arg = :Shell
    when '-w','--writer'    then next_arg = :writer
    when '-o','--output'    then next_arg = :output
    when '-l','--logfile'   then next_arg = :logfile
    else
      if next_arg
        ARGS[next_arg] = arg
        UNFLAGGED_ARGS.delete( next_arg )
      end
      next_arg = UNFLAGGED_ARGS.first
  end
end

puts "DocuBot v#{DocuBot::VERSION}" if ARGS[:version]

if ARGS[:help] or !ARGS[:directory]
  puts USAGE unless ARGS[:version]
  puts HELP if ARGS[:help]
  exit
end

if ARGS[:logfile]
  $stdout.reopen( ARGS[:logfile], "w" )
  $stdout.sync = true
  $stderr.reopen( $stdout )
end

# etc.
28
Phrogz

Rubys eingebauter OptionParser macht dies gut. Kombinieren Sie es mit OpenStruct und Sie sind zu Hause frei:

require 'optparse'

options = {}
OptionParser.new do |opt|
  opt.on('--first_name FIRSTNAME') { |o| options[:first_name] = o }
  opt.on('--last_name LASTNAME') { |o| options[:last_name] = o }
end.parse!

puts options

options enthält die Parameter und Werte als Hash.

Das Speichern und Ausführen in der Befehlszeile ohne Parameter führt zu:

$ Ruby test.rb
{}

Ausführen mit Parametern:

$ Ruby test.rb --first_name=foo --last_name=bar
{:first_name=>"foo", :last_name=>"bar"}

In diesem Beispiel wird ein Hash verwendet, um die Optionen zu enthalten. Sie können jedoch eine OpenStruct verwenden, die wie Ihre Anfrage verwendet wird:

require 'optparse'
require 'ostruct'

options = OpenStruct.new
OptionParser.new do |opt|
  opt.on('-f', '--first_name FIRSTNAME', 'The first name') { |o| options.first_name = o }
  opt.on('-l', '--last_name LASTNAME', 'The last name') { |o| options.last_name = o }
end.parse!

puts options.first_name + ' ' + options.last_name

$ Ruby test.rb --first_name=foo --last_name=bar
foo bar

Es erstellt sogar automatisch Ihre Option -h oder --help:

$ Ruby test.rb -h
Usage: test [options]
        --first_name FIRSTNAME
        --last_name LASTNAME

Sie können auch kurze Flags verwenden:

require 'optparse'

options = {}
OptionParser.new do |opt|
  opt.on('-f', '--first_name FIRSTNAME') { |o| options[:first_name] = o }
  opt.on('-l', '--last_name LASTNAME') { |o| options[:last_name] = o }
end.parse!

puts options

Das durch seine Schritte gehen:

$ Ruby test.rb -h
Usage: test [options]
    -f, --first_name FIRSTNAME
    -l, --last_name LASTNAME
$ Ruby test.rb -f foo --l bar
{:first_name=>"foo", :last_name=>"bar"}

Es ist einfach, Inline-Erklärungen für die Optionen hinzuzufügen:

OptionParser.new do |opt|
  opt.on('-f', '--first_name FIRSTNAME', 'The first name') { |o| options[:first_name] = o }
  opt.on('-l', '--last_name LASTNAME', 'The last name') { |o| options[:last_name] = o }
end.parse!

und:

$ Ruby test.rb -h
Usage: test [options]
    -f, --first_name FIRSTNAME       The first name
    -l, --last_name LASTNAME         The last name

OptionParser unterstützt auch die Konvertierung des Parameters in einen Typ, z. B. eine Ganzzahl oder ein Array. Weitere Beispiele und Informationen finden Sie in der Dokumentation.

Sie sollten sich auch die zugehörige Fragenliste auf der rechten Seite ansehen:

85
the Tin Man

Ich persönlich benutze Docopt . Dies ist viel klarer, wartbarer und esay lesend.

In online Ruby Implementation doc finden Sie Beispiele. Die Verwendung ist wirklich unkompliziert.

gem install docopt

Ruby-Code:

doc = <<DOCOPT
My program who says hello

Usage:
  #{__FILE__} --first_name=<first_name> --last_name=<last_name>
DOCOPT

begin
  args = Docopt::docopt(doc)
rescue Docopt::Exit => e
  puts e.message
  exit
end

print "Hello #{args['--first_name']} #{args['--last_name']}"

Dann anrufen:

$ ./says_hello.rb --first_name=Homer --last_name=Simpsons
Hello Homer Simpsons

Und ohne Argumente:

$ ./says_hello.rb
Usage:
  says_hello.rb --first_name=<first_name> --last_name=<last_name>
2
brunetton

Ein bisschen Standard Ruby Regexp in myscript.rb:

args = {}

ARGV.each do |arg|
  match = /--(?<key>.*?)=(?<value>.*)/.match(arg)
  args[match[:key]] = match[:value] # e.g. args['first_name'] = 'donald'
end

puts args['first_name'] + ' ' + args['last_name']

Und in der Kommandozeile:

$ Ruby script.rb --first_name=donald --last_name=knuth

Produziert: 

$ donald knuth

1
Marty Cortez

Eine verbesserte Version, die Argumente behandelt, die keine Optionen sind, Argumente mit einem Parameter und -a sowie --a.

def parse(args)
  parsed = {}

  args.each do |arg|
    match = /^-?-(?<key>.*?)(=(?<value>.*)|)$/.match(arg)
    if match
      parsed[match[:key].to_sym] = match[:value]
    else
      parsed[:text] = "#{parsed[:text]} #{arg}".strip
    end
  end

  parsed
end
0
Chap Chipley