Skip to content

Preserve literal backslash before variable - #550

Open
MsfPablo wants to merge 1 commit into
bkeepers:mainfrom
MsfPablo:fix/backslash-before-variable-substitution
Open

Preserve literal backslash before variable#550
MsfPablo wants to merge 1 commit into
bkeepers:mainfrom
MsfPablo:fix/backslash-before-variable-substitution

Conversation

@MsfPablo

Copy link
Copy Markdown

What

Preserves a literal \ before $VAR references so RESULT="a\\$FOO" with FOO=bar yields RESULT="a\bar" (one backslash + the substituted value) instead of either dropping the backslash silently or substituting nothing.

Why

Issue #549 reported that \$VAR was treated as escape + nothing, but \\$VAR was being collapsed by the parser's existing unescape_characters step (regex /\\([^$])/) before variable substitution ever ran, so substitution got $VAR and the \ was already gone. The reporter's expectation: a doubled \\ is a literal backslash, which the substitution layer should then preserve when expanding $FOO.

Changes

lib/dotenv/parser.rb (1 line) — unescape_characters regex tightened from /\\([^$])/ to /\\([^$\\])/. A \ followed by another \ is no longer collapsed here, so the double-backslash reaches variable substitution intact.

lib/dotenv/substitutions/variable.rb (~6 lines) — VARIABLE regex gains a leading (\\\\)? group (matched against a double backslash). New branch: when match[1] is the double backslash, emit "\\" + value — one literal backslash, then the substituted value. Existing single-backslash escape and bare-variable branches behave the same as before; their match indices shifted by one ($2, $3, $4 instead of $1, $2, $3).

spec/dotenv/parser_spec.rb (4 lines) — new test preserves a literal backslash before a variable reference (#549). Asserts the input above yields RESULT=a\bar.

Test plan

  • rspec spec/dotenv/parser_spec.rb — 59 examples, 0 failures (includes the new test)
  • rspec --exclude-pattern "spec/dotenv/log_subscriber_spec.rb,spec/dotenv/rails_spec.rb" — 165 examples, 0 failures (the two excluded specs require unavailable active_support/rails gems and are unrelated to this change)
  • bundle exec rspec blocked by a sandbox-tooling parsing issue on this machine; raw rspec from the user gem dir reproduces the same 165/0 result

Notes

  • Headline behavior now matches shell quoting: \\$FOO means "literal backslash, then expand $FOO"; \$FOO still means "escape, leave $FOO literal".
  • No shift in behavior for callers already on the simple $VAR or \$VAR paths — only \\$VAR changes.

Disclosed: this PR was prepared with LLM assistance under the MsfPablo persona and reviewed by the human author before submission.

unescape_characters now leaves an unescaped double backslash
(backslash not followed by $ or another backslash) intact so that
Variable substitution can detect the literal-backslash + variable
pattern introduced by the parser. Variable substitution recognizes
this pattern and emits a literal backslash followed by the
substituted value.

AI-assisted: This patch was prepared with assistance from Claude (Anthropic) and reviewed by the human author before submission.

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR adjusts Dotenv parsing/substitution to preserve a literal backslash immediately before variable references (e.g. RESULT="a\\$FOO" with FOO=bar should become RESULT="a\bar"), addressing Issue #549.

Changes:

  • Tweaks Parser#unescape_characters so \\ is not collapsed before substitution.
  • Extends variable substitution matching to treat a doubled backslash before $VAR as “emit one backslash + expanded value”.
  • Adds an RSpec regression test covering the reported scenario.

Reviewed changes

Copilot reviewed 3 out of 3 changed files in this pull request and generated 3 comments.

File Description
lib/dotenv/parser.rb Changes unescape behavior for backslashes prior to running substitutions.
lib/dotenv/substitutions/variable.rb Adds special handling for a doubled backslash immediately before a variable reference.
spec/dotenv/parser_spec.rb Adds a regression test for preserving a literal backslash before $VAR.

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment on lines +26 to +30
if match[1] == "\\\\"
"\\" + (env[match[4]] || ENV[match[4]] || "")
elsif match[2] == "\\"
variable[1..]
elsif match[3]
env[match[3]] || ENV[match[3]] || ""
elsif match[4]
Comment thread lib/dotenv/parser.rb

def unescape_characters(value)
value.gsub(/\\([^$])/, '\1')
value.gsub(/\\([^$\\])/, '\1')
Comment on lines +20 to +22
it "preserves a literal backslash before a variable reference (#549)" do
expect(env("FOO=bar\nRESULT=\"a\\\\$FOO\"")).to eql("FOO" => "bar", "RESULT" => "a\\bar")
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants