在Ruby脚本中解析命令行参数
我想从命令行调用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 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