diff --git a/crates/bashkit/src/interpreter/mod.rs b/crates/bashkit/src/interpreter/mod.rs index 81a534b3..f36b208f 100644 --- a/crates/bashkit/src/interpreter/mod.rs +++ b/crates/bashkit/src/interpreter/mod.rs @@ -9727,4 +9727,26 @@ echo "count=$COUNT" let result = bash.exec(r#"printf "a\nb\nc" | wc -l"#).await.unwrap(); assert_eq!(result.stdout.trim(), "2"); } + + #[tokio::test] + async fn test_regex_match_from_variable() { + // Issue #400: [[ =~ $var ]] should work with regex from variable + let mut bash = crate::Bash::new(); + let result = bash + .exec(r#"re="200"; line="hello 200 world"; [[ $line =~ $re ]] && echo "match" || echo "no""#) + .await + .unwrap(); + assert_eq!(result.stdout.trim(), "match"); + } + + #[tokio::test] + async fn test_regex_match_literal() { + // Issue #400: literal regex should still work + let mut bash = crate::Bash::new(); + let result = bash + .exec(r#"line="hello 200 world"; [[ $line =~ 200 ]] && echo "match" || echo "no""#) + .await + .unwrap(); + assert_eq!(result.stdout.trim(), "match"); + } } diff --git a/crates/bashkit/src/parser/mod.rs b/crates/bashkit/src/parser/mod.rs index 3a3bebcb..3270f0d6 100644 --- a/crates/bashkit/src/parser/mod.rs +++ b/crates/bashkit/src/parser/mod.rs @@ -1263,10 +1263,20 @@ impl<'a> Parser<'a> { let is_literal = matches!(self.current_token, Some(tokens::Token::LiteralWord(_))); - // After =~, collect the regex pattern (may contain parens) + // After =~, handle regex pattern. + // If the pattern contains $ (variable reference), parse it as a + // normal word so variables expand. Otherwise collect as literal + // regex to preserve parens, backslashes, etc. if saw_regex_op { - let pattern = self.collect_conditional_regex_pattern(&w_clone); - words.push(Word::literal(&pattern)); + if w_clone.contains('$') && !is_quoted { + // Variable reference — parse normally for expansion + let parsed = self.parse_word(w_clone); + words.push(parsed); + self.advance(); + } else { + let pattern = self.collect_conditional_regex_pattern(&w_clone); + words.push(Word::literal(&pattern)); + } saw_regex_op = false; continue; }