Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
40 changes: 38 additions & 2 deletions crates/bashkit/src/interpreter/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5288,9 +5288,45 @@ impl Interpreter {
}
}

// For other words, expand to a single field
// For other words, expand to a single field then apply IFS word splitting
// when the word is unquoted and contains a command substitution.
// Per POSIX, unquoted $() results undergo field splitting on IFS.
let expanded = self.expand_word(word).await?;
Ok(vec![expanded])

let has_command_subst = !word.quoted
&& word
.parts
.iter()
.any(|p| matches!(p, WordPart::CommandSubstitution(_)));

if has_command_subst {
// Split on IFS characters (default: space, tab, newline).
// Consecutive IFS-whitespace characters are collapsed (no empty fields).
let ifs = self
.variables
.get("IFS")
.cloned()
.unwrap_or_else(|| " \t\n".to_string());

if ifs.is_empty() {
// Empty IFS: no splitting
return Ok(vec![expanded]);
}

let fields: Vec<String> = expanded
.split(|c: char| ifs.contains(c))
.filter(|s| !s.is_empty())
.map(|s| s.to_string())
.collect();

if fields.is_empty() {
// All-whitespace expansion produces zero fields (elision)
return Ok(Vec::new());
}
Ok(fields)
} else {
Ok(vec![expanded])
}
}

/// Apply parameter expansion operator
Expand Down
33 changes: 33 additions & 0 deletions crates/bashkit/tests/spec_cases/bash/command-subst.test.sh
Original file line number Diff line number Diff line change
Expand Up @@ -152,3 +152,36 @@ echo "$(echo "hello\"world")"
### expect
hello"world
### end

### subst_word_split_for_loop
# Command substitution output is word-split in for-loop list context
count=0
for f in $(printf '/src/one.txt\n/src/two.txt\n/src/three.txt\n'); do
count=$((count + 1))
done
echo "$count"
### expect
3
### end

### subst_word_split_echo_multiword
# Command substitution producing space-separated words splits in for-loop
result=""
for w in $(echo "alpha beta gamma"); do
result="${result}[${w}]"
done
echo "$result"
### expect
[alpha][beta][gamma]
### end

### subst_word_split_newlines
# Command substitution with newline-separated output splits on newlines
result=""
for line in $(printf 'x\ny\nz'); do
result="${result}(${line})"
done
echo "$result"
### expect
(x)(y)(z)
### end
Loading