diff --git a/crates/bashkit/src/interpreter/mod.rs b/crates/bashkit/src/interpreter/mod.rs index b81e408b..c69770af 100644 --- a/crates/bashkit/src/interpreter/mod.rs +++ b/crates/bashkit/src/interpreter/mod.rs @@ -3265,7 +3265,21 @@ impl Interpreter { | WordPart::ArrayAccess { .. } ) }); - if is_unquoted_expansion { + // "${arr[@]}" or "$@" in array context should splat + // individual elements, not join into a single string. + let is_quoted_splat = word.quoted + && word.parts.len() == 1 + && matches!( + &word.parts[0], + WordPart::ArrayAccess { index, .. } if index == "@" + ); + let is_quoted_positional_splat = word.quoted + && word.parts.len() == 1 + && matches!( + &word.parts[0], + WordPart::Variable(name) if name == "@" + ); + if is_unquoted_expansion || is_quoted_splat || is_quoted_positional_splat { let fields = self.expand_word_to_fields(word).await?; all_fields.extend(fields); } else { diff --git a/crates/bashkit/tests/spec_cases/bash/array-splat.test.sh b/crates/bashkit/tests/spec_cases/bash/array-splat.test.sh new file mode 100644 index 00000000..146d9da2 --- /dev/null +++ b/crates/bashkit/tests/spec_cases/bash/array-splat.test.sh @@ -0,0 +1,22 @@ +### array_concat_at_expansion +# "${arr[@]}" in array literal should splat individual elements +a=(x y z) +b=(1 2) +c=("${a[@]}" "${b[@]}") +echo "${#c[@]}" +echo "${c[2]}" +### expect +5 +z +### end + +### array_splat_single_source +# Single "${arr[@]}" in array context +orig=(a b c d) +copy=("${orig[@]}") +echo "${#copy[@]}" +echo "${copy[3]}" +### expect +4 +d +### end