diff --git a/ruby/shell/word_expander.rb b/ruby/shell/word_expander.rb index 48c4170..f1bbf7c 100644 --- a/ruby/shell/word_expander.rb +++ b/ruby/shell/word_expander.rb @@ -18,11 +18,19 @@ module Shell protected_line = protect_escaped_dollars(line) substituted_line = expand_command_substitution(protected_line) shellsplit(substituted_line) - .map do |word| - expand_variables(word) + .flat_map do |word| + expanded = expand_variables(word) .tr(ESCAPED_DOLLAR, "$") .tr(ESCAPED_BACKTICK, "`") - # TODO: expand globs + expand_braces(expanded) + end + .flat_map do |word| + if word =~ /[*?\[]/ + glob_words = expand_globs(word) + glob_words.empty? ? [word] : glob_words + else + [word] + end end end @@ -268,6 +276,20 @@ module Shell stdout.tr("\n", " ") end + def expand_braces(word) + # Simple, non-nested brace expansion: pre{a,b}post -> preapost, prebpost + match = word.match(/(.*?)\{([^{}]*)\}(.*)/) + return [word] unless match + + prefix = match[1] + body = match[2] + suffix = match[3] + return [word] unless body.include?(",") + + parts = body.split(",", -1) + parts.flat_map { |part| expand_braces(prefix + part + suffix) } + end + def escaped_replacement(char) case char when "$" diff --git a/ruby/test/shell_test.rb b/ruby/test/shell_test.rb index 3c747a8..664c895 100644 --- a/ruby/test/shell_test.rb +++ b/ruby/test/shell_test.rb @@ -63,7 +63,6 @@ class ShellTest < Minitest::Test end def test_expands_brace_expansion - skip "brace expansion not implemented" assert_equal "a b", `#{A1_PATH} -c 'echo {a,b}'`.chomp end @@ -99,7 +98,7 @@ class ShellTest < Minitest::Test end def test_does_not_expand_escaped_command_substitution_dollar_paren_in_double_quotes - assert_equal "$(echo hi)", %x(#{A1_PATH} -c 'echo "\\$(echo hi)"').chomp + assert_equal "$(echo hi)", `#{A1_PATH} -c 'echo "\\$(echo hi)"'`.chomp end def test_does_not_expand_escaped_command_substitution_backticks_in_double_quotes