在命令行参数中指定nil

时间:2012-05-03 20:23:35

标签: ruby bash command-line-interface

我有一个命令行工具,可以生成一个扁平的json doc。想象一下:

$ ruby gen-json.rb --foo bar --baz qux
{
    "foo": "bar"
    "baz": "qux"
}

我想要的是这个工作:

$ ruby gen-json.rb --foo $'\0' --baz qux
{
    "foo": null,
    "baz": "qux"
}

而不是null我得到一个空字符串。为了简化问题,请进一步考虑:

$ cat nil-args.rb
puts "argv[0] is nil" if ARGV[0].nil?
puts "argv[0] is an empty string" if ARGV[0] == ""
puts "argv[1] is nil" if ARGV[1].nil?

我想像这样运行它并获得此输出:

$ ruby nil-args.rb $'\0' foo
argv[0] is nil

但我得到了

argv[0] is an empty string

我怀疑这是(可以说)ruby解释器中的一个错误。它将argv [0]视为一个空字符串终止的C字符串。

4 个答案:

答案 0 :(得分:3)

命令行参数始终字符串。您需要使用哨兵来表示您想要处理的参数。

答案 1 :(得分:1)

我很确定你字面不能做你提出的建议。这是您正在使用的shell的基本限制。您只能将字符串参数传递给脚本。

在评论中已经提到过,但你尝试使用\ 0方法得到的输出非常有意义。 null终止符在技术上是一个空字符串。

还要考虑访问尚未定义的数组的任何元素将始终为nil。

a = [1, 2, 3]
a[10].nil?
#=> true

然而,一个可能的解决方案是让你的程序像这样工作:

$ ruby gen-json.rb --foo --baz qux
{
    "foo": null,
    "baz": "qux"
}

因此,如果您有一个双减号参数后跟另一个双减号参数,则推断第一个参数为空。但是,您需要编写自己的命令行选项解析器来实现此目的。

这是一个非常简单的示例脚本,似乎有效(但可能有边缘情况和其他问题):

require 'pp'

json = {}

ARGV.each_cons(2) do |key, value|
  next unless key.start_with? '--'
  json[key] = value.start_with?('--') ? nil : value
end

pp json

这是否适用于您的目的? :)

答案 2 :(得分:0)

Ruby将除nilfalse之外的所有内容视为true。因此,任何空字符串都将评估为true(以及00.0)。

你可以通过这样的方式强制零行为:

ARGV.map! do |arg|
  if arg.empty?
    nil
  else
    arg
  end
end

这样,任何空字符串都将在对nil的引用中进行转换。

答案 3 :(得分:0)

你将不得不决定一个特殊的角色来表示零。我建议"\0"(双引号确保传入文字字符串反斜杠零)。然后,您可以在OptionParser中使用特殊类型转换器:

require 'optparse'
require 'json'

values = {}
parser = OptionParser.new do |opts|
  opts.on("--foo VAL",:nulldetect) { |val| values[:foo] = val }
  opts.on("--bar VAL",:nulldetect) { |val| values[:foo] = val }

  opts.accept(:nulldetect) do |string|
    if string == '\0'
      nil
    else
      string
    end
  end
end

parser.parse!

puts values.to_json

显然,这要求你明确你接受的标志,但如果你想接受任何标志,你当然可以用if语句来检查"\0"

作为旁注,你可以让标志是可选的,但你得到传递给块的空字符串,例如

opts.on("--foo [VAL]") do |val|
  # if --foo is passed w/out args, val is empty string
end

所以,你还需要一个替补