编辑:请请,请在回复之前阅读本文底部列出的两项要求。人们不断发布他们的新宝石和图书馆等等,这显然不符合要求。
有时我想非常便宜地将一些命令行选项破解成一个简单的脚本。一个有趣的方法,不用处理getopts或解析或类似的东西,是:
...
$quiet = ARGV.delete('-d')
$interactive = ARGV.delete('-i')
...
# Deal with ARGV as usual here, maybe using ARGF or whatever.
这不是普通的Unix选项语法,因为它会接受选项非选项命令行参数,如“myprog -i foo bar -q
”,但我可以忍受。 (有些人,比如Subversion的开发人员,更喜欢这个。有时我也会这样做。)
只是存在或不存在的选项不能比上述更简单地实现。 (一个赋值,一个函数调用,一个副作用。)是否有一种同样简单的方法来处理带参数的选项,例如“-f
filename ”?
我之前没有说过的一点,因为直到Trollop的作者提到图书馆适合“在一个[800行]文件中”之前我还没有弄清楚,我看起来不是仅用于干净的语法,但对于具有以下特征的技术:
整个代码可以包含在脚本文件中(不会压倒实际的脚本本身,可能只有几十行),这样就可以在{{1在任何具有标准Ruby 1.8。[5-7]的系统上安装并使用它。如果你不能编写一个没有require语句的Ruby脚本,并且解析几个选项的代码大概在十几行左右,你就不能满足这个要求。
代码很小而且非常简单,人们可以记住足够的代码来直接输入代码来完成这一操作,而不是从其他地方剪切和粘贴。想想你所在的防火墙服务器控制台没有互联网访问权限的情况,你想把一个快速的脚本拼凑起来供客户使用。我不了解你,但是(除了上面的要求失败之外)记住甚至45行简化的微光谱也不是我想做的事情。
答案 0 :(得分:233)
作为Trollop的作者,我无法相信人们认为在选项解析器中合理的东西。认真。它令人难以置信。
为什么我必须创建一个扩展其他模块的模块来解析选项?为什么我必须继承任何东西?为什么我只需要订阅一些“框架”来解析命令行?
这是以上的Trollop版本:
opts = Trollop::options do
opt :quiet, "Use minimal output", :short => 'q'
opt :interactive, "Be interactive"
opt :filename, "File to process", :type => String
end
就是这样。 opts
现在是一个包含密钥:quiet
,:interactive
和:filename
的哈希。你可以用它做任何你想做的事。你会得到一个漂亮的帮助页面,格式适合你的屏幕宽度,自动短参数名称,类型检查......你需要的一切。
这是一个文件,因此如果您不想要正式的依赖项,可以将它放在lib /目录中。它有一个很容易上手的最小DSL。
LOC每个选项的人。这很重要。
答案 1 :(得分:76)
我与require 'getopts'
分享您的厌恶,主要是因为OptionParser
的真棒:
% cat temp.rb
require 'optparse'
OptionParser.new do |o|
o.on('-d') { |b| $quiet = b }
o.on('-i') { |b| $interactive = b }
o.on('-f FILENAME') { |filename| $filename = filename }
o.on('-h') { puts o; exit }
o.parse!
end
p :quiet => $quiet, :interactive => $interactive, :filename => $filename
% ruby temp.rb
{:interactive=>nil, :filename=>nil, :quiet=>nil}
% ruby temp.rb -h
Usage: temp [options]
-d
-i
-f FILENAME
-h
% ruby temp.rb -d
{:interactive=>nil, :filename=>nil, :quiet=>true}
% ruby temp.rb -i
{:interactive=>true, :filename=>nil, :quiet=>nil}
% ruby temp.rb -di
{:interactive=>true, :filename=>nil, :quiet=>true}
% ruby temp.rb -dif apelad
{:interactive=>true, :filename=>"apelad", :quiet=>true}
% ruby temp.rb -f apelad -i
{:interactive=>true, :filename=>"apelad", :quiet=>nil}
答案 2 :(得分:59)
这是我经常使用的标准技术:
#!/usr/bin/env ruby
def usage(s)
$stderr.puts(s)
$stderr.puts("Usage: #{File.basename($0)}: [-l <logfile] [-q] file ...")
exit(2)
end
$quiet = false
$logfile = nil
loop { case ARGV[0]
when '-q' then ARGV.shift; $quiet = true
when '-l' then ARGV.shift; $logfile = ARGV.shift
when /^-/ then usage("Unknown option: #{ARGV[0].inspect}")
else break
end; }
# Program carries on here.
puts("quiet: #{$quiet} logfile: #{$logfile.inspect} args: #{ARGV.inspect}")
答案 3 :(得分:34)
由于没有人提及它,并且标题 引用廉价命令行解析,为什么不让红宝石翻译为你做这项工作呢?如果您通过-s
开关(例如,在您的shebang中),您可以免费获得简单的开关,分配给单字母全局变量。以下是使用该开关的示例:
#!/usr/bin/env ruby -s
puts "#$0: Quiet=#$q Interactive=#$i, ARGV=#{ARGV.inspect}"
这是我将其保存为./test
并将其保存为+x
时的输出:
$ ./test
./test: Quiet= Interactive=, ARGV=[]
$ ./test -q foo
./test: Quiet=true Interactive=, ARGV=["foo"]
$ ./test -q -i foo bar baz
./test: Quiet=true Interactive=true, ARGV=["foo", "bar", "baz"]
$ ./test -q=very foo
./test: Quiet=very Interactive=, ARGV=["foo"]
有关详细信息,请参阅ruby -h
。 :)
必须便宜得多。如果你尝试像-:
这样的开关,它会引发一个NameError,所以那里有一些验证。当然,在非切换参数之后你不能有任何开关,但如果你需要一些花哨的东西,你真的应该在最小的OptionParser上使用。事实上,唯一令我烦恼的是这种技术,当你访问一个未设置的全局变量时,你会得到一个警告(如果你已经启用它们),但它仍然是假的,所以它适用于一次性工具和快速脚本。
一个警告(as pointed out by FelipeC in the comments)是你的shell可能不支持3-token shebang;您可能需要将/usr/bin/env ruby -w
替换为ruby的实际路径(如/usr/local/bin/ruby -w
),或者从包装脚本或其他内容运行它。
答案 4 :(得分:13)
我构建了micro-optparse来满足这个对一个简短但易于使用的选项解析器的明显需求。它的语法类似于Trollop,短70行。如果您不需要验证而且没有空行,则可以将其减少到45行。我认为这正是你所寻找的。 p>
简短的例子:
options = Parser.new do |p|
p.version = "fancy script version 1.0"
p.option :verbose, "turn on verbose mode"
p.option :number_of_chairs, "defines how many chairs are in the classroom", :default => 1
p.option :room_number, "select room number", :default => 2, :value_in_set => [1,2,3,4]
end.process!
使用-h
或--help
调用脚本将打印
Usage: micro-optparse-example [options]
-v, --[no-]verbose turn on verbose mode
-n, --number-of-chairs 1 defines how many chairs are in the classroom
-r, --room-number 2 select room number
-h, --help Show this message
-V, --version Print version
它检查输入是否与默认值相同,生成短访问者和长访问者,如果给出无效参数则打印描述性错误消息等。
我compared several option-parser通过使用每个选项解析器来解决我遇到的问题。 您可以使用这些示例和我的摘要做出信息性决策。 随意添加更多实现到列表中。 :)
答案 5 :(得分:8)
我完全理解为什么你要避免使用optparse - 它可能会得到太多。但是有一些远远低于“轻量级”的解决方案(与OptParse相比)作为库而已,但它足够简单,可以使单个宝石安装变得有价值。
例如,请查看this OptiFlag example。只需几行处理。根据您的情况量身定制的略微截断的示例:
require 'optiflag'
module Whatever extend OptiFlagSet
flag "f"
and_process!
end
ARGV.flags.f # => .. whatever ..
有tons of customized examples too。我记得使用另一种更容易的东西,但它现在已经逃脱了,但如果我找到它,我会回来在这里添加评论。
答案 6 :(得分:4)
这是我用于真正,非常便宜的args:
def main
ARGV.each { |a| eval a }
end
main
所以如果你运行programname foo bar
,它会调用foo然后调用bar。这对于一次性脚本来说非常方便。
答案 7 :(得分:3)
您可以尝试以下内容:
if( ARGV.include( '-f' ) )
file = ARGV[ARGV.indexof( '-f' ) + 1 )]
ARGV.delete('-f')
ARGV.delete(file)
end
答案 8 :(得分:3)
您是否考虑过wycats Thor?我认为它比optparse更清洁。如果您已经编写了一个脚本,那么可能需要更多的工作来格式化它或者将其重构为thor,但它确实使处理选项变得非常简单。
以下是自述文件中的示例代码段:
class MyApp < Thor # [1]
map "-L" => :list # [2]
desc "install APP_NAME", "install one of the available apps" # [3]
method_options :force => :boolean, :alias => :optional # [4]
def install(name)
user_alias = options[:alias]
if options.force?
# do something
end
# ... other code ...
end
desc "list [SEARCH]", "list all of the available apps, limited by SEARCH"
def list(search = "")
# list everything
end
end
Thor会自动映射命令:
app install myname --force
转换为:
MyApp.new.install("myname")
# with {'force' => true} as options hash
答案 9 :(得分:3)
这是我最喜欢的快速选项解析器:
case ARGV.join
when /-h/
puts "help message"
exit
when /-opt1/
puts "running opt1"
end
选项是正则表达式,所以&#34; -h&#34;也会匹配&#34; - help&#34;。
可读,易记,无外部库和最少的代码。
答案 10 :(得分:2)
Trollop相当便宜。
答案 11 :(得分:2)
如果你想要一个简单的命令行解析器来获取key / value命令而不使用gems:
但如果你总是拥有键/值对,那么只有才有效。
# example
# script.rb -u username -p mypass
# check if there are even set of params given
if ARGV.count.odd?
puts 'invalid number of arguments'
exit 1
end
# holds key/value pair of cl params {key1 => value1, key2 => valye2, ...}
opts = {}
(ARGV.count/2).times do |i|
k,v = ARGV.shift(2)
opts[k] = v # create k/v pair
end
# set defaults if no params are given
opts['-u'] ||= 'root'
# example use of opts
puts "username:#{opts['-u']} password:#{opts['-p']}"
如果您不需要检查,则可以使用:
opts = {}
(ARGV.count/2).times do |i|
k,v = ARGV.shift(2)
opts[k] = v # create k/v pair
end
答案 12 :(得分:2)
这是我大多数脚本顶部使用的代码段:
arghash = Hash.new.tap { |h| # Parse ARGV into a hash
i = -1
ARGV.map{ |s| /(-[a-zA-Z_-])?([^=]+)?(=)?(.+)?/m.match(s).to_a }
.each{ |(_,a,b,c,d)| h[ a ? "#{a}#{b}#{c}" : (i+=1) ] =
(a ? (c ? "#{d}" : true) : "#{b}#{c}#{d}")
}
[[:argc,Proc.new {|| h.count{|(k,_)| !k.is_a?(String)}}],
[:switches, Proc.new {|| h.keys.select{|k| k[0] == '-' }}]
].each{|(n,p)| h.define_singleton_method(n,&p) }
}
我也讨厌在快捷脚本中要求其他文件。我的解决方案几乎是您的要求。我将10行代码片段粘贴到我的任何脚本的顶部,这些脚本将分析命令行并将位置args粘贴并切换到Hash对象(通常分配给我命名为 arghash 的对象)在以下示例中)。
这是您可能想解析的示例命令行...
./myexampleprog.rb -s -x=15 --longswitch arg1 --longswitch2=val1 arg2
将成为这样的哈希。
{
'-s' => true,
'-x=' => '15',
'--longswitch' => true,
'--longswitch2=' => 'val1',
0 => 'arg1',
1 => 'arg2'
}
除此之外,还为哈希添加了两种便捷方法:
argc()
将返回非切换参数的计数。switches()
将返回一个数组,其中包含存在的开关的键这意味着允许一些快速而肮脏的东西,例如...
arghash.argc == 2
)的切换如何arghash[1]
始终获得第二个非开关参数)。arghash['--max=']
访问,给定示例命令行,该值将产生值“ 15”。arghash['-s']
之类的非常简单的符号测试命令行中是否存在开关。使用
之类的设置操作测试是否存在开关或其他开关 puts USAGETEXT if !(%w(-h --help) & arghash.switches()).empty?
通过设置操作(例如
)来识别无效开关的使用 puts "Invalid switch found!" if !(arghash.switches - %w(-valid1 -valid2)).empty?
使用简单的Hash.merge()
指定缺少参数的默认值,例如以下示例,如果未设置-max =,则填充-max =的值;如果未传递,则添加第4个位置参数
with_defaults = {'-max=' => 20, 3 => 'default.txt'}.merge(arghash)
答案 13 :(得分:1)
这与接受的答案非常相似,但使用-l=file
这是我在simple parser中使用的内容。唯一真正的区别是带参数的选项必须在一起(例如def usage
"usage: #{File.basename($0)}: [-l=<logfile>] [-q] file ..."
end
$quiet = false
$logfile = nil
ARGV.delete_if do |cur|
next false if cur[0] != '-'
case cur
when '-q'
$quiet = true
when /^-l=(.+)$/
$logfile = $1
else
$stderr.puts "Unknown option: #{cur}"
$stderr.puts usage
exit 1
end
end
puts "quiet: #{$quiet} logfile: #{$logfile.inspect} args: #{ARGV.inspect}"
)。
#!/opt/local/bin/python3.6
import sys, subprocess
class child_proc:
def __init__ (self, useShell):
print ("useShell:", useShell)
acmd = ["/tmp/a.pl", "Hello", "World"]
if useShell:
proc = subprocess.Popen(acmd, stdout=subprocess.PIPE, shell=True)
else:
proc = subprocess.Popen(acmd, stdout=subprocess.PIPE)
while True:
line = proc.stdout.readline()
if line:
try:
aline = line.decode().rstrip()
print (aline)
except UnicodeDecodeError as e:
print("Error decoding child output:", line, e)
break
else:
break
child_proc(len(sys.argv) > 1)
答案 14 :(得分:0)
显然@WilliamMorgan和我一样。我刚刚在Github上发布了我刚刚发布的内容,我在Github上搜索了OptionParser后,看到了与Trollop类似的库(命名如何?),请参阅Switches
有一些差异,但哲学是一样的。一个明显的区别是Switch依赖于OptionParser。
答案 15 :(得分:0)
我正在开发my own option parser gem called Acclaim。
我编写它是因为我想创建git风格的命令行界面,并且能够将每个命令的功能干净地分成单独的类,但它也可以在没有整个命令框架的情况下使用:
(options = []) << Acclaim::Option.new(:verbose, '-v', '--verbose')
values = Acclaim::Option::Parser.new(ARGV, options).parse!
puts 'Verbose.' if values.verbose?
目前还没有稳定发布,但我已经实现了一些功能,如:
有很多强调命令,所以对于简单的命令行解析可能有点重,但它运行良好,我一直在我的所有项目中使用它。如果您对命令界面方面感兴趣,请查看the project's GitHub page以获取更多信息和示例。
答案 16 :(得分:0)
假设一个命令最多只有一个动作和任意数量的选项,如下所示:
cmd.rb
cmd.rb action
cmd.rb action -a -b ...
cmd.rb action -ab ...
没有验证的解析可能是这样的:
ACTION = ARGV.shift
OPTIONS = ARGV.join.tr('-', '')
if ACTION == '***'
...
if OPTIONS.include? '*'
...
end
...
end
答案 17 :(得分:0)
https://github.com/soveran/clap
other_args = Clap.run ARGV,
"-s" => lambda { |s| switch = s },
"-o" => lambda { other = true }
46LOC(1.0.0),不依赖于外部选项解析器。完成工作。可能不像其他人那样功能齐全,但它是46LOC。
如果你检查代码,你可以很容易地复制基础技术 - 分配lambdas并使用arity来确保如果你真的不想要一个外部库,那么args的正确数量会跟着标志。
简单。便宜。
编辑:基础概念已经失效,因为我认为您可以将其复制/粘贴到脚本中以制作合理的命令行解析器。它肯定不我会承诺记忆,但使用lambda arity作为一个廉价的解析器是一个新颖的想法:
flag = false
option = nil
opts = {
"--flag" => ->() { flag = true },
"--option" => ->(v) { option = v }
}
argv = ARGV
args = []
while argv.any?
item = argv.shift
flag = opts[item]
if flag
raise ArgumentError if argv.size < arity
flag.call(*argv.shift(arity))
else
args << item
end
end
# ...do stuff...
答案 18 :(得分:-1)
我将分享我自己一直在研究的简单选项解析器。它只有74行代码,它完成了Git内部选项解析器的基础知识。我把OptionParser作为灵感,也是Git的。
https://gist.github.com/felipec/6772110
看起来像这样:
opts = ParseOpt.new
opts.usage = "git foo"
opts.on("b", "bool", help: "Boolean") do |v|
$bool = v
end
opts.on("s", "string", help: "String") do |v|
$str = v
end
opts.on("n", "number", help: "Number") do |v|
$num = v.to_i
end
opts.parse
答案 19 :(得分:-1)
EasyOptions根本不需要任何解析代码的选项。只需编写帮助文本,require,done。
## Options:
## -i, --interactive Interactive mode
## -q, --quiet Silent mode
require 'easyoptions'
unless EasyOptions.options[:quiet]
puts 'Interactive mode enabled' if EasyOptions.options[:interactive]
EasyOptions.arguments.each { |item| puts "Argument: #{item}" }
end