From 44dcfa7ba6f763dfef93537fd2a6f95382dc76a1 Mon Sep 17 00:00:00 2001 From: Sami Samhuri Date: Mon, 2 Feb 2026 20:48:13 -0800 Subject: [PATCH] Expand default values recursively --- ruby/shell/word_expander.rb | 22 ++++++++++++++++------ ruby/test/shell_test.rb | 4 ++++ 2 files changed, 20 insertions(+), 6 deletions(-) diff --git a/ruby/shell/word_expander.rb b/ruby/shell/word_expander.rb index 5726036..4b0b0b2 100644 --- a/ruby/shell/word_expander.rb +++ b/ruby/shell/word_expander.rb @@ -3,6 +3,7 @@ require "shellwords" module Shell class WordExpander ENV_VAR_REGEX = /\$(?:\{([^}]+)\}|(\w+)\b)/ + DEFAULT_VAR_REGEX = /\A(\w+):-([\s\S]*)\z/ ESCAPED_DOLLAR = "\u0001" # Splits the given line into multiple words, performing the following transformations: @@ -15,12 +16,7 @@ module Shell protected_line = protect_escaped_dollars(line) shellsplit(protected_line) .map do |word| - word - .gsub(ENV_VAR_REGEX) do - name = Regexp.last_match(2) || Regexp.last_match(1) - ENV.fetch(name) - end - .tr(ESCAPED_DOLLAR, "$") + expand_variables(word).tr(ESCAPED_DOLLAR, "$") # TODO: expand globs end end @@ -98,6 +94,20 @@ module Shell Dir.glob(word) end + def expand_variables(value) + value.gsub(ENV_VAR_REGEX) do + raw = Regexp.last_match(2) || Regexp.last_match(1) + if (m = DEFAULT_VAR_REGEX.match(raw)) + name = m[1] + fallback = m[2] + env_value = ENV[name] + env_value.nil? || env_value.empty? ? expand_variables(fallback) : env_value + else + ENV.fetch(raw) + end + end + end + def protect_escaped_dollars(line) output = +"" i = 0 diff --git a/ruby/test/shell_test.rb b/ruby/test/shell_test.rb index 7aa3c85..aaf960f 100644 --- a/ruby/test/shell_test.rb +++ b/ruby/test/shell_test.rb @@ -88,6 +88,10 @@ class ShellTest < Minitest::Test assert_equal "fallback", `#{A1_PATH} -c 'echo ${A1_UNSET_VAR:-fallback}'`.chomp end + def test_expands_parameter_default_value_with_variable_reference + assert_equal Dir.home, `#{A1_PATH} -c 'echo ${A1_UNSET_VAR:-$HOME}'`.chomp + end + ################################# ### Execution and job control ### #################################