Объясните аргументы командной строки в Ruby script
Я хочу вызвать Ruby script из командной строки и передать параметры, которые являются парами ключ/значение.
Вызов командной строки:
$ ruby my_script.rb --first_name=donald --last_name=knuth
my_script.rb:
puts args.first_name + args.last_name
Каков стандартный способ Ruby для этого? В других языках я обычно должен использовать парсер параметров. В Ruby я видел, что мы имеем ARGF.read
, но, похоже, не работают пары ключ/значение, как в этом примере.
OptionParser выглядит многообещающим, но я не могу сказать, действительно ли он поддерживает этот случай.
Ответы
Ответ 1
В соответствии с ответом от @MartinCortez здесь короткий одноразовый, который создает хэш пар ключ/значение, где значения должны быть соединены знаком =
. Он также поддерживает аргументы флага без значений:
args = Hash[ ARGV.join(' ').scan(/--?([^=\s]+)(?:=(\S+))?/) ]
... или альтернативно...
args = Hash[ ARGV.flat_map{|s| s.scan(/--?([^=\s]+)(?:=(\S+))?/) } ]
Вызывается -x=foo -h --jim=jam
, он возвращает {"x"=>"foo", "h"=>nil, "jim"=>"jam"}
, поэтому вы можете делать такие вещи, как:
puts args['jim'] if args.key?('h')
#=> jam
В то время как для этого есть несколько библиотек, в том числе GetoptLong
, включенных в Ruby - я лично предпочитаю рулон самостоятельно. Здесь используется шаблон, который делает его достаточно общим, не привязан к определенному формату использования и достаточно гибким, чтобы допускать смешанные флаги, параметры и требуемые аргументы в разных порядках:
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.
Ответ 2
Ruby встроенный OptionParser делает это красиво. Объедините его с OpenStruct и вы свободны:
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
будет содержать параметры и значения как хэш.
Сохранение и запуск в командной строке без параметров приводит к:
$ ruby test.rb
{}
Запуск с параметрами:
$ ruby test.rb --first_name=foo --last_name=bar
{:first_name=>"foo", :last_name=>"bar"}
В этом примере используется Hash для включения параметров, но вы можете использовать OpenStruct, который приведет к использованию, например, вашему запросу:
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
Он даже автоматически создает вашу опцию -h
или --help
:
$ ruby test.rb -h
Usage: test [options]
--first_name FIRSTNAME
--last_name LASTNAME
Вы также можете использовать короткие флажки:
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
Выполнение этого по его шагам:
$ 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"}
Также легко добавить встроенные объяснения для параметров:
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!
и
$ ruby test.rb -h
Usage: test [options]
-f, --first_name FIRSTNAME The first name
-l, --last_name LASTNAME The last name
OptionParser также поддерживает преобразование параметра в тип, например Integer или Array. Дополнительную информацию и примеры см. В документации.
Вы также должны просмотреть список связанных вопросов справа:
Ответ 3
Я использую Docopt. Это гораздо более понятное, удобное и удобное чтение.
Посмотрите на онлайн-документ Ruby для реализации. Использование действительно просто.
gem install docopt
Код Ruby:
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']}"
Затем вызов:
$ ./says_hello.rb --first_name=Homer --last_name=Simpsons
Hello Homer Simpsons
И без аргументов:
$ ./says_hello.rb
Usage:
says_hello.rb --first_name=<first_name> --last_name=<last_name>
Ответ 4
Немного стандартного Ruby Regexp в 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']
И в командной строке:
$ ruby script.rb --first_name=donald --last_name=knuth
Производит:
$ donald knuth
Ответ 5
Улучшенная версия, которая обрабатывает аргументы, которые не являются опциями, аргументы с параметром и -a
а также --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