将命令行参数解析为通配符

时间:2010-10-17 18:59:15

标签: ruby arrays optparse

我写了一个简单的脚本,它将所有给定的参数写入单个文本文件,由换行符分隔。我想使用OptionParser将文件列表传递给它。我想使用/dir/*等通配符添加几个文件。

我试过了:

opts = OptionParser.new 
opts.on('-a', '--add FILE') do |s| 
  puts "DEBUG: before #{s}"
  @options.add = s
  puts "DEBUG: after #{@options.add}"
end
...
def process_arguments
  @lines_to_add = Dir.glob @options.add
end

当我添加这样的文件时:

./script.rb -a /path/*

我总是只获得目录中的第一个文件。所有调试输出只显示目录的第一个文件,似乎OptionParser做了一些神奇的解释

有谁知道如何处理这个问题?

4 个答案:

答案 0 :(得分:4)

您没有提到您正在使用的操作系统(这很重要)。

在Windows上,无论您在命令行上键入什么,都会传递给程序而不进行修改。所以,如果你输入

./script.rb -a /path/*

然后该程序的参数包含"-a""/path/*"

在Unix和其他具有类似shell的系统上,shell执行参数扩展,它会自动扩展命令行中的通配符。因此,当您在上面键入相同的命令时, shell 会查找/path/*目录中的文件,并在程序运行之前展开命令行参数。因此,您的计划的参数可能是"-a""/path/file1""/path/file2"

重要的一点是,脚本无法确定参数扩展是否发生,或者用户是否在命令行中实际输入了所有这些文件名。

答案 1 :(得分:3)

如上所述,在操作系统将命令交给Ruby之前,正在解析命令行。通配符正在扩展为以空格分隔的文件名列表。

你可以看到如果在命令行输入类似echo *的内容会发生什么,然后不是点击返回,而是点击 Esc 然后 * 。您应该看到*已扩展到匹配文件列表中。

点击返回后,这些名称将被添加到ARGV数组中。 OptionParser将遍历ARGV并找到您定义的标志,必要时抓取以下元素,然后从ARGV中删除它们。当OptionParser完成时,任何不适合选项的ARGV元素将保留在ARGV数组中,您可以在其中获取它们。

在您的代码中,您正在为'-a''--add FILE'选项寻找单个参数。 OptionParser有一个Array选项,它将从命令行中获取逗号分隔的元素,但后续以空格分隔的元素。

require 'optparse'

options = []
opts = OptionParser.new 
opts.on('-a', '--add FILE', Array) do |s| 
  options << s
end.parse!

print "options => ", options.join(', '), "\n"
print "ARGV => ",    ARGV.join(', '),    "\n"

将其保存到文件并尝试使用-a one two three命令行,然后使用-a one,two,three。您将看到Array选项如何根据参数之间是否有逗号或空格来区分元素。

因为*通配符被空格分隔的文件名替换,所以在OptionParser针对它运行后,您必须对ARGV进行后处理,或者以编程方式对目录进行全局处理并以此方式构建列表。 ARGV拥有除-a选项中选择的文件以外的所有文件,因此,我个人会删除-a选项并让ARGV包含所有文件。

如果*包含太多文件且超出缓冲区大小,则必须对目录进行全局操作。你会知道是否会发生这种情况,因为操作系统会抱怨。

答案 2 :(得分:0)

正在发生的事情是shell在Ruby到达之前扩展了通配符。所以你真正在处理:

./script.rb -a /path/file1 /path/file2 ......

/path/*周围加上引号以避免shell扩展并将通配符传递给Ruby:

./script.rb -a '/path/*'

答案 3 :(得分:0)

shell在将参数传递给程序之前将其展开。要么继续使用文件名,直到找到另一个选项,要么让用户转义通配符(例如./script.rb -a '/path/*')并自己将它们全局化。