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,但这似乎不像这个例子那样工作键/值对。
OptionParser looks promising, but I can't tell if it actually supports this case.
OptionParser看起来很有希望,但我不知道它是否真的支持这种情况。
4 个解决方案
#1
26
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=jam
it 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 GetoptLong
included 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:
虽然有多个库可以处理这个 - 包括Ruby中包含的GetoptLong - 我个人更喜欢自己动手。这是我使用的模式,它使其合理通用,不依赖于特定的使用格式,并且足够灵活,允许在各种顺序中混合使用标记,选项和必需参数:
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
76
Ruby's built-in OptionParser does this nicely. Combine it with OpenStruct and 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
options
will 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:
该示例使用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
It even automatically creates your -h
or --help
option:
它甚至会自动创建-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还支持将参数转换为类型,例如Integer或Array。有关更多示例和信息,请参阅文档。
You should also look at the related questions list to the right:
您还应该查看右侧的相关问题列表:
- "Really Cheap Command-Line Option Parsing in Ruby"
- “真正便宜的命令行选项在Ruby中解析”
- "Pass variables to Ruby script via command line"
- “通过命令行将变量传递给Ruby脚本”
#3
2
A bit of standard Ruby Regexp in myscript.rb
:
myscript.rb中的一些标准Ruby Regexp:
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
$ ruby script.rb --first_name = donald --last_name = knuth
Produces:
生产:
$ donald knuth
$ donald knuth
#4
1
I personnaly use Docopt. This is much more clear, maintainable and esay-reading.
我个人使用Docopt。这更清晰,更易于维护和阅读。
Have a look to online Ruby implementation doc for examples. Usage is really straightforward.
有关示例,请查看在线Ruby实现文档。用法非常简单。
gem install docopt
Ruby code:
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']}"
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>
#1
26
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=jam
it 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 GetoptLong
included 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:
虽然有多个库可以处理这个 - 包括Ruby中包含的GetoptLong - 我个人更喜欢自己动手。这是我使用的模式,它使其合理通用,不依赖于特定的使用格式,并且足够灵活,允许在各种顺序中混合使用标记,选项和必需参数:
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
76
Ruby's built-in OptionParser does this nicely. Combine it with OpenStruct and 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
options
will 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:
该示例使用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
It even automatically creates your -h
or --help
option:
它甚至会自动创建-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还支持将参数转换为类型,例如Integer或Array。有关更多示例和信息,请参阅文档。
You should also look at the related questions list to the right:
您还应该查看右侧的相关问题列表:
- "Really Cheap Command-Line Option Parsing in Ruby"
- “真正便宜的命令行选项在Ruby中解析”
- "Pass variables to Ruby script via command line"
- “通过命令行将变量传递给Ruby脚本”
#3
2
A bit of standard Ruby Regexp in myscript.rb
:
myscript.rb中的一些标准Ruby Regexp:
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
$ ruby script.rb --first_name = donald --last_name = knuth
Produces:
生产:
$ donald knuth
$ donald knuth
#4
1
I personnaly use Docopt. This is much more clear, maintainable and esay-reading.
我个人使用Docopt。这更清晰,更易于维护和阅读。
Have a look to online Ruby implementation doc for examples. Usage is really straightforward.
有关示例,请查看在线Ruby实现文档。用法非常简单。
gem install docopt
Ruby code:
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']}"
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>