Skip to content

\$ inside double quotes expands as variable instead of literal $ #948

@chaliy

Description

@chaliy

Summary

In double-quoted strings, \$ should produce a literal $ character. Bashkit instead treats the $ as a variable expansion marker after the backslash is stripped, causing unintended variable expansion.

Reproduction

# FAIL — \$ should be literal
echo "\$HOME"
# expected: $HOME
# actual:   /root (or whatever HOME is set to)

# FAIL — with set -u, causes unbound error
set -u
echo "\$nonexistent"
# expected: $nonexistent
# actual:   bash: nonexistent: unbound variable

# FAIL — breaks embedded perl/awk
perl -e "\$x = 42; print \$x"
# expected: perl sees "$x = 42; print $x"
# actual:   bash expands $x before perl sees it

# PASS — single quotes (no expansion, verify no regression)
echo '\$HOME'
# expected: \$HOME
# actual:   \$HOME ✓

Spec tests to add

### backslash_dollar_in_double_quotes
# \$ in double quotes should produce literal $
echo "\$HOME"
### expect
$HOME
### end

### backslash_dollar_with_text
# \$ followed by identifier should not expand
echo "\$foo bar"
### expect
$foo bar
### end

### backslash_dollar_set_u
# \$ should not trigger unbound variable under set -u
set -u
echo "\$undefined_var"
### expect
$undefined_var
### end

### backslash_dollar_in_assignment
# Assignment with \$ should preserve literal
x="\$PATH"
echo "$x"
### expect
$PATH
### end

Where to fix

The lexer at crates/bashkit/src/parser/lexer.rs (around line 1111) correctly converts \$ to a $ character. But then parse_word() at crates/bashkit/src/parser/mod.rs:2539 re-interprets the resulting $ as a variable expansion marker.

The fix needs to distinguish between a $ that came from \$ escaping (should be literal) and a genuine $ expansion marker. Options:

  1. Have the lexer emit an escape-sentinel that parse_word() recognizes as literal
  2. Have the lexer produce a distinct token/character for escaped-dollar
  3. Track escape origin in the character stream so parse_word() can skip expansion

Real-world impact

Critical\$ in double quotes is extremely common in bash scripts that embed other languages (perl, awk, python). The wedow/harness str_replace tool uses perl -i -0pe "\$old = quotemeta(q{...})" which fails completely in bashkit. Any script using echo "\$var" for literal output also breaks.

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions