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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/26434923/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-06 06:36:38  来源:igfitidea点击:

Parse command line arguments in a Ruby script

rubycommand-line

提问by Don P

I want to call a Ruby script from the command line, and pass in parameters that are key/value pairs.

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

Command line call:

命令行调用:

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

my_script.rb:

my_script.rb:

puts args.first_name + args.last_name

What is the standard Ruby way to do this? In other languages I usually have to use an option parser. In Ruby I saw we have ARGF.read, but that does not seem to work key/value pairs like in this example.

执行此操作的标准 Ruby 方法是什么?在其他语言中,我通常必须使用选项解析器。在 Ruby 中,我看到我们有ARGF.read,但这似乎不像在这个例子中那样工作键/值对。

OptionParserlooks promising, but I can't tell if it actually supports this case.

OptionParser看起来很有希望,但我不知道它是否真的支持这种情况。

采纳答案by Phrogz

Based on the answer by @MartinCortez here's a short one-off that makes a hash of key/value pairs, where the values must be joined with an =sign. It also supports flag arguments without values:

根据@MartinCortez 的回答,这里有一个简短的一次性方法,它生成键/值对的散列,其中值必须用=符号连接。它还支持没有值的标志参数:

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

…or alternatively…

……或者……

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

Called with -x=foo -h --jim=jamit returns {"x"=>"foo", "h"=>nil, "jim"=>"jam"}so you can do things like:

-x=foo -h --jim=jam它调用返回{"x"=>"foo", "h"=>nil, "jim"=>"jam"}所以你可以做这样的事情:

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


While there are multiple libraries to handle this—including GetoptLongincluded with Ruby—I personally prefer to roll my own. Here's the pattern I use, which makes it reasonably generic, not tied to a specific usage format, and flexible enough to allow intermixed flags, options, and required arguments in various orders:

虽然有多个库来处理这个问题——包括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.

回答by the Tin Man

Ruby's built-in OptionParserdoes this nicely. Combine it with OpenStructand you're home free:

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

optionswill contain the parameters and values as a hash.

options将包含散列形式的参数和值。

Saving and running that at the command line with no parameters results in:

在不带参数的命令行中保存并运行它会导致:

$ ruby test.rb
{}

Running it with parameters:

使用参数运行它:

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

That example is using a Hash to contain the options, but you can use an OpenStruct which will result in usage like your request:

该示例使用哈希来包含选项,但您可以使用 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

It even automatically creates your -hor --helpoption:

它甚至会自动创建您的-h--help选项:

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

You can use short flags too:

您也可以使用短标志:

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

Running that through its paces:

运行它的步伐:

$ 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"}

It's easy to add inline explanations for the options too:

为选项添加内联解释也很容易:

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!

and:

和:

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

OptionParser also supports converting the parameter to a type, such as an Integer or an Array. Refer to the documentation for more examples and information.

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

You should also look at the related questions list to the right:

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

回答by Marty Cortez

A bit of standard Ruby Regexpin myscript.rb:

有点Ruby标准的正则表达式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']

And on the command line:

在命令行上:

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

Produces:

产生:

$ donald knuth

回答by brunetton

I personally use Docopt. This is much more clear, maintainable and easy to read.

我个人使用Docopt。这更加清晰,可维护且易于阅读。

Have a look at the Ruby implementation's documentationfor examples. The usage is really straightforward.

查看 Ruby 实现的文档以获取示例。用法非常简单。

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']}"

Then calling:

然后调用:

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

And without arguments:

并且没有论据:

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

回答by localhostdotdev

An improved version that handles arguments that are not options, arguments with a parameter, and -aas well as --a.

处理不是选项的参数、带参数的参数-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

回答by whatbox

Here is a slight modification to @Phrogz excellent answer: this mod will allow you to pass a string with spaces in it.

这是对@Phrogz 优秀答案的轻微修改:此 mod 将允许您传递一个包含空格的字符串。

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

In a command line pass the string like this:

在命令行中传递这样的字符串:

ruby my_script.rb '--first="Boo Boo" --last="Bear"'

Or from another ruby script like this:

或者来自另一个像这样的 ruby​​ 脚本:

system('ruby my_script.rb \'--first="Boo Boo" --last="Bear"\'')

Results:

结果:

{"first"=>"Boo Boo", "last"=>"Bear"}