在Ruby脚本中解析命令行参数

在Ruby脚本中解析命令行参数,ruby,command-line,Ruby,Command Line,我想从命令行调用Ruby脚本,并传入键/值对参数 命令行调用: $ ruby my_script.rb --first_name=donald --last_name=knuth my_script.rb: puts args.first_name + args.last_name Ruby的标准方法是什么?在其他语言中,我通常必须使用选项解析器。在Ruby中,我看到我们有ARGF.read,但这似乎不像本例中的键/值对 看起来很有希望,但我不知道它是否真的支持这种情况。myscript.r

我想从命令行调用Ruby脚本,并传入键/值对参数

命令行调用:

$ ruby my_script.rb --first_name=donald --last_name=knuth
my_script.rb:

puts args.first_name + args.last_name
Ruby的标准方法是什么?在其他语言中,我通常必须使用选项解析器。在Ruby中,我看到我们有
ARGF.read
,但这似乎不像本例中的键/值对

看起来很有希望,但我不知道它是否真的支持这种情况。
myscript.rb中的标准Ruby:

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']
产生:

$ donald knuth

根据@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

虽然有多个库来处理这个问题,包括-我个人更喜欢自己的。以下是我使用的模式,它使其具有合理的通用性,不受特定使用格式的限制,并且足够灵活,允许以各种顺序混合使用标志、选项和必需的参数:

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.
USAGE=Ruby的内置功能很好地做到了这一点。将其与以下功能结合使用,您就可以自由回家了:

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
选项
将包含散列形式的参数和值

在没有参数的命令行中保存并运行该命令将导致:

$ ruby test.rb
{}
使用参数运行它:

$ ruby test.rb --first_name=foo --last_name=bar
{:first_name=>"foo", :last_name=>"bar"}
该示例使用散列来包含选项,但您可以使用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!
以及:

OptionParser还支持将参数转换为类型,例如整数或数组。有关更多示例和信息,请参阅文档

您还应查看右侧的相关问题列表:

  • “”
  • “”
    • 我个人使用。这更加清晰、易于维护和易于阅读

      看看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
      Usage:
        says_hello.rb --first_name=<first_name> --last_name=<last_name>
      
      $。/says_hello.rb
      用法:
      说_hello.rb--first_name=--last_name=
      
      一个改进版本,用于处理非选项的参数、带参数的参数和
      -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
      
      def解析(args)
      已解析={}
      args.每个do | arg|
      匹配=/^-?-(?*)(?*)$/.match(arg)
      如果匹配
      已分析的[match[:key]。to_sym]=匹配[:value]
      其他的
      已解析[:text]=“#{parsed[:text]}{arg}”
      结束
      结束
      解析
      结束
      
      这里是对@Phrogz的一个轻微修改,这是一个极好的回答:这个修改将允许你传递一个包含空格的字符串。

      args= Hash[ ARGV.join(' ').scan(/--?([^=\s]+)(?:="(.*?)"+)?/)] args=Hash[ARGV.join(“”).scan(/-?([^=\s]+)(?:=“(.*?”+)?/)] 在命令行中,按如下方式传递字符串:

      ruby my_script.rb '--first="Boo Boo" --last="Bear"' ruby my_script.rb'-first=“Boo Boo”--last=“Bear” 或者来自另一个ruby脚本,如下所示:

      system('ruby my_script.rb \'--first="Boo Boo" --last="Bear"\'') 系统('ruby my\u script.rb\'-first=“Boo Boo”--last=“Bear”\“”) 结果:

      {"first"=>"Boo Boo", "last"=>"Bear"}
      {“first”=>“Boo Boo”,“last”=>“Bear”}Ruby中有许多命令行参数解析器:

      • -包含在标准数据库中
      • -不再是
        stdlib
        的一部分,转换为单独的
        optpasse
        gem
      就个人而言,我会选择
      slop
      optimist
      ,它们不是标准Ruby安装的一部分

      gem install slop
      
      但它提供了简单性和代码可读性。假设示例稍微复杂一些,带有必需的参数和默认值:

      require 'slop'
      
      begin
        opts = Slop.parse do |o|
          o.int '-a', '--age', 'Current age', default: 42
          o.string '-f', '--first_name', 'The first name', required: true
          o.string '-l', '--last_name', 'The last name', required: true
          o.bool '-v', '--verbose', 'verbose output', default: false
          o.on '-h','--help', 'print the help' do
            puts o
            exit
          end
        end
      
        p opts.to_hash
      rescue Slop::Error => e
        puts e.message
      end
      
      optimist
      以前称为
      trollop
      ,它非常容易准备,只需最少的样板代码:

      gem install optimist
      
      使用
      OptionParser
      的类似示例:

      #!/usr/bin/env ruby
      
      require 'optparse'
      require 'ostruct'
      
      begin
        options = OpenStruct.new
        OptionParser.new do |opt|
          opt.on('-a', '--age AGE', 'Current age') { |o| options.age = o }
          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 }
          opt.on('-v', '--verbose', 'Verbose output') { |o| options.verbose = true }
        end.parse!
      
        options[:age] = 42 if options[:age].nil?
        raise OptionParser::MissingArgument.new('--first_name') if options[:first_name].nil?
        raise OptionParser::MissingArgument.new('--last_name') if options[:last_name].nil?
        options[:verbose] = false if options[:verbose].nil?
      
      rescue OptionParser::ParseError => e
        puts e.message
        exit
      end
      
      GetoptLong
      解析更为复杂:

      require 'getoptlong'
      
      opts = GetoptLong.new(
        [ '--help', '-h', GetoptLong::NO_ARGUMENT ],
        [ '--first_name', '-f', GetoptLong::REQUIRED_ARGUMENT ],
        [ '--last_name', '-l', GetoptLong::REQUIRED_ARGUMENT ],
        [ '--age','-a', GetoptLong::OPTIONAL_ARGUMENT ],
        [ '--verbose','-v', GetoptLong::OPTIONAL_ARGUMENT ]
      )
      begin
        options = {}
        options[:verbose] = false
        options[:age] = 42
        opts.each do |opt, arg|
          case opt
          when '--help'
              puts <<-EOF
        usage: ./getlongopts.rb [options]
      
            -a, --age         Current age
            -f, --first_name  The first name
            -l, --last_name   The last name
            -v, --verbose     verbose output
            -h, --help        print the help
      
              EOF
          when '--first_name'
            options[:first_name] = arg
          when '--last_name'
            options[:last_name] = arg
          when '--age'
            options[:age] = arg.to_i
          when '--verbose'
            options[:verbose] = arg
          else
            puts "unknown option `#{opt}`"
            exit 1
          end
        end
      
        raise GetoptLong::MissingArgument.new('Missing argument --first_name') if options[:first_name].nil?
        raise GetoptLong::MissingArgument.new('Missing argument --last_name') if options[:last_name].nil?
      
      rescue GetoptLong::Error => e
        puts e.message
        exit
      end
      
      puts options
      
      需要“getoptlong”
      opts=GetoptLong.new(
      ['--help','-h',GetoptLong::NO_参数],
      ['--first_name','-f',GetoptLong::REQUIRED_参数],
      ['--last_name','-l',GetoptLong::必需的_参数],
      ['--age','-a',GetoptLong::可选的_参数],
      ['--verbose','-v',GetoptLong::可选的_参数]
      )
      开始
      选项={}
      选项[:verbose]=false
      选项[:年龄]=42
      opts.each do | opt,arg|
      案例选择
      当“救命”
      
      如果我没弄错的话,Highline看起来像是请求用户输入的助手函数。因此,我会使用Highline让我的控制台说出
      名字:
      ,然后等待他们的输入。有没有一个特别的功能,我应该看看它?有很多宝石,你可以选择;该网站对图书馆进行分类,并根据受欢迎程度对其进行排序。我甚至编写了自己的gem,名为
      aclawim
      ,它确实支持
      --option=value
      语法。不过,我还没有时间维护我的自由软件项目。你应该选择一个更好的支持库。这里有一个关于选项解析器的好教程:这很好!使用
      =
      来消除
      -f foo
      -x-y bar
      之间的歧义,对于我来说,在现实世界中使用起来有点困难,但这是一个很好的快速破解方法。我只是按照上面的格式。基于这个问题,我不确定他是否关心消歧。这看起来很棒,但你会如何定义
      s
      <代码>ARGV.join.to_s
      ?我再也看不到它了(你的评论)是的,我删除了我的评论,因为正如你指出的,它是错误的。:)现在,我的答案就是这样。删除其他注释以进行清理。这很好,但如果您也使用内置的
      ARGF
      通过文件名/stdin读取流,则需要确保使用
      ARGV
      gem install optimist
      
      require 'optimist'
      
      opts = Optimist::options do
        opt :verbose, "verbose mode"
        opt :first_name, "The first name", type: :string, required: true
        opt :last_name, "The last name", type: :string, required: true
        opt :age, "Current age", default: 42
      end
      
      p opts
      
      #!/usr/bin/env ruby
      
      require 'optparse'
      require 'ostruct'
      
      begin
        options = OpenStruct.new
        OptionParser.new do |opt|
          opt.on('-a', '--age AGE', 'Current age') { |o| options.age = o }
          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 }
          opt.on('-v', '--verbose', 'Verbose output') { |o| options.verbose = true }
        end.parse!
      
        options[:age] = 42 if options[:age].nil?
        raise OptionParser::MissingArgument.new('--first_name') if options[:first_name].nil?
        raise OptionParser::MissingArgument.new('--last_name') if options[:last_name].nil?
        options[:verbose] = false if options[:verbose].nil?
      
      rescue OptionParser::ParseError => e
        puts e.message
        exit
      end
      
      require 'getoptlong'
      
      opts = GetoptLong.new(
        [ '--help', '-h', GetoptLong::NO_ARGUMENT ],
        [ '--first_name', '-f', GetoptLong::REQUIRED_ARGUMENT ],
        [ '--last_name', '-l', GetoptLong::REQUIRED_ARGUMENT ],
        [ '--age','-a', GetoptLong::OPTIONAL_ARGUMENT ],
        [ '--verbose','-v', GetoptLong::OPTIONAL_ARGUMENT ]
      )
      begin
        options = {}
        options[:verbose] = false
        options[:age] = 42
        opts.each do |opt, arg|
          case opt
          when '--help'
              puts <<-EOF
        usage: ./getlongopts.rb [options]
      
            -a, --age         Current age
            -f, --first_name  The first name
            -l, --last_name   The last name
            -v, --verbose     verbose output
            -h, --help        print the help
      
              EOF
          when '--first_name'
            options[:first_name] = arg
          when '--last_name'
            options[:last_name] = arg
          when '--age'
            options[:age] = arg.to_i
          when '--verbose'
            options[:verbose] = arg
          else
            puts "unknown option `#{opt}`"
            exit 1
          end
        end
      
        raise GetoptLong::MissingArgument.new('Missing argument --first_name') if options[:first_name].nil?
        raise GetoptLong::MissingArgument.new('Missing argument --last_name') if options[:last_name].nil?
      
      rescue GetoptLong::Error => e
        puts e.message
        exit
      end
      
      puts options