fix(shellenv): preserve literal newlines in exported env values (ambiguous redirect) - #2846
Conversation
exportify escaped newlines in env-var values by writing a backslash
before the newline. Inside a bash double-quoted string, a
backslash-newline is a line continuation, so the shell removes both
characters and concatenates adjacent lines. A multi-line value such as a
PROMPT_COMMAND set by bash-preexec would therefore be silently mangled,
e.g. "... >/dev/null 2>&1\n__bp_interactive_mode" collapsed into
"... 2>&1__bp_interactive_mode", producing the redirect target
"1__bp_interactive_mode" and the error:
bash: 1__bp_interactive_mode: ambiguous redirect
Newlines are not special inside double quotes, so they must be left
unescaped to be preserved literally. Add a regression test covering
multi-line values alongside the existing special-character escaping.
Fixes #2814
There was a problem hiding this comment.
Pull request overview
Fixes a bash quoting bug in devbox global shellenv / generated shellrc exports where multi-line environment values (notably PROMPT_COMMAND) were being mangled due to emitting backslash-newline sequences inside double quotes, which bash treats as a line continuation and removes.
Changes:
- Stop escaping
'\n'inexportifyso multi-line env values preserve literal newlines inside double quotes. - Add focused unit tests for
exportify, including special-character escaping and the multi-line regression from #2814.
Reviewed changes
Copilot reviewed 2 out of 2 changed files in this pull request and generated no comments.
| File | Description |
|---|---|
| internal/devbox/envvars.go | Updates bash export escaping rules to preserve literal newlines in double-quoted values, avoiding line-continuation behavior. |
| internal/devbox/envvars_test.go | Adds TestExportify to cover simple exports, special-character escaping, and the multi-line newline-preservation regression. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
The varnamelen linter flagged the single-letter loop variable 'r' because the added explanatory comment lengthened the loop body scope. Move the comment above the for loop; behavior is unchanged.
…rg-JuE0j # Conflicts: # internal/devbox/envvars_test.go
|
@claude please review this PR for correctness, and flag anything that would block merging. |
|
Claude finished @mikeland73's task in 2m 56s —— View job Review of PR #2846
Verdict: Correct. No blockers. Root-cause analysis checks outThe diagnosis is accurate: inside a POSIX double-quoted string, I traced the interaction with the other escaped characters to make sure nothing regresses:
Checked all consumers of
|
Summary
Fixes #2814.
eval "$(devbox global shellenv)"produced the following error on every new prompt for users whose shell sets a multi-linePROMPT_COMMAND(e.g. viabash-preexec, as the elementary OS terminal does):Root cause
exportify(internal/devbox/envvars.go) buildsexport KEY="value";statements and escaped newlines inside the value by writing a backslash before the newline:Inside a bash double-quoted string, a backslash-newline is a line continuation: the shell removes both the backslash and the newline and joins the adjacent lines. So a multi-line value like:
was emitted as
... 2>&1\<newline>__bp_interactive_modeand collapsed by the shell into... 2>&1__bp_interactive_mode. The redirect target became1__bp_interactive_mode, hence the ambiguous redirect error.Newlines are not special inside double quotes (see the POSIX shell quoting rules already cited in the code) — they are preserved literally. The fix is simply to stop escaping them.
Quick demonstration of the difference:
Changes
internal/devbox/envvars.go: drop'\n'from the set of charactersexportifyescapes, with a comment explaining why newlines must stay literal. The other shell-special characters ($,`,",\) are still escaped.internal/devbox/envvars_test.go: newTestExportifycovering simple values, special-character escaping, and the multi-line regression from this issue.This affects both
devbox shellenvoutput and the env exports written into the generated shellrc; in both contexts the output is sourced as bash, so literal newlines inside double quotes are correct.How was it tested?
go test ./internal/devbox/ -run TestExportify -v— passes.go build ./internal/devbox/andgo vet ./internal/devbox/— clean.cc @proedie (reporter) — thanks for the detailed write-up and the precise pinpointing of the offending line; that made this a quick fix.
🤖 Generated with Claude Code
Generated by Claude Code