我想获取一个包含位置参数标记(未命名)的字符串,为其提供值的数组(而非哈希),并对其进行评估。
用例作为示例有点像ARGV。
例如,
# given:
string = "echo $1 ; echo $@"
values = ["hello", "world"]
# expected result:
"echo hello ; echo hello world"
以下功能是我能想到的最好的功能:
def evaluate_args(string, arguments)
return string unless arguments.is_a? Array and !arguments.empty?
# Create a variable that can replace $@ with all arguments, and quote
# arguments that had "more than one word" originally
all_arguments = arguments.map{|a| a =~ /\s/ ? "\"#{a}\"" : a}.join ' '
# Replace all $1 - $9 with their respective argument ($1 ==> arguments[0])
string.gsub!(/\$(\d)/) { arguments[$1.to_i - 1] }
# Replace $@ or $* with all arguments
string.gsub!(/\$[*|@]/, all_arguments)
return string
end
在我看来,它似乎可以而且应该更简单。
我希望能找到更接近Kernel.sprintf
做事方法的东西 - 比如"string with %{marker}" % {marker: 'value'}
所以,虽然这个问题几乎已经解决了(我认为),但我很想知道是否有一些我错过的东西可以让它更优雅。
答案 0 :(得分:2)
您似乎正在尝试重现Bash风格的变量扩展,这是一个非常复杂的问题。但至少,您可以通过两种方式简化代码:
Kernel.sprintf
的内置位置参数功能。以下代码通过替换例如$1
sprintf
等效%1$s
。require 'shellwords'
def evaluate_args(string, arguments)
return string unless arguments.is_a? Array and !arguments.empty?
tmpl = string.gsub(/\$(\d+)/, '%\1$s')
(tmpl % arguments).gsub(/\$[*@]/, arguments.shelljoin)
end
string = "echo $1 ; echo $@"
values = ["hello", "world"]
puts evaluate_args(string, values)
# => echo hello ; echo hello world
如果您没有$*
要求,我建议您只删除类似Bash的格式并使用sprintf
,因为它涵盖了您提到的其他所有内容。即便如此,您还可以使用sprintf
格式化其他所有内容来进一步简化操作:
def evaluate_args(string, arguments)
return string unless arguments.is_a? Array and !arguments.empty?
string.gsub('%@', arguments.shelljoin) % arguments
end
string = "echo %1$s ; echo %@"
values = ["hello", "world"]
puts evaluate_args(string, values)
# => echo hello ; echo hello world
如果要将%{1}
与sprintf
一起使用,可以将输入数组转换为散列,其中整数索引转换为符号键,例如["hello", "world"]
变为{ :"1" => "hello", :"2" => "world" }
:
require "shellwords"
def evaluate_args(string, arguments)
return string unless arguments.is_a? Array and !arguments.empty?
string % {
:* => arguments.shelljoin,
**arguments.map.with_index {|val,idx| [ :"#{idx + 1}", val ] }.to_h
}
end
string = "echo %{1} ; echo %{*}"
values = ["hello", "world"]
puts evaluate_args(string, values)
# => echo hello ; echo hello world
答案 1 :(得分:1)
string = "echo $1 ; echo $@ ; echo $2 ; echo $cat"
values = ["hello", "World War II"]
vals = values.map { |s| s.include?(' ') ? "\"#{s}\"" : s }
#=> ["hello", "\"World War II\""]
all_vals = vals.join(' ')
#=> "hello \"World War II\""
string.gsub(/\$\d+|\$[@*]/) { |s| s[/\$\d/] ? vals[s[1..-1].to_i-1] : all_vals }
#=> "echo hello ; echo hello \"World War II\" ; echo \"World War II\" ; echo $cat" $cat"