Skip to content

Commit 4b7160e

Browse files
authored
nix: fix shellrc PATH word splitting in bash (#176)
Fix the loop in `shellrc.tmpl` so that `devbox shell` doesn't end up with a broken `PATH` delimited by spaces instead of colons. This was due to a subtle bug related to how bash and zsh do word splitting differently. Read on for an in-depth explanation. We fix up the `PATH` in `shellrc.tmpl` by looping over each `PATH` directory. To get each dir, we set `IFS=:` and rely on the shell's word splitting to loop over `PATH`. A caveat to this approach is that zsh doesn't follow the POSIX behavior for word splitting. Here's an example showing the difference: bash: bash$ export x=a:b:c bash$ export IFS=: bash$ echo $x a b c zsh: zsh$ export x=a:b:c zsh$ export IFS=: zsh$ echo $x a:b:c To workaround this, we use command substitution to force zsh to word split: zsh$ export x=a:b:c zsh$ export IFS=: zsh$ echo $(echo $x) a b c There's a subtle bug here that causes the splitting to break in bash because the splitting happens too soon (note the single quotes around the first echo output): bash$ export x=a:b:c bash$ export IFS=: bash$ set -x; echo $(echo $x) ++ echo a b c + echo 'a b c' a b c The subshell splits `$x`, resulting in 'a b c'. The parent shell doesn't split 'a b c' because it has already been split and doesn't have any colons. This doesn't happen in zsh, because zsh doesn't split unquoted variables. Quoting `$x` stops bash from splitting in the subshell, fixing this for both shells: bash$ export x=a:b:c bash$ export IFS=: bash$ set -x; echo $(echo "$x") ++ echo a:b:c + echo a b c a b c Fixes #175.
1 parent baf33c8 commit 4b7160e

File tree

4 files changed

+4
-4
lines changed

4 files changed

+4
-4
lines changed

nix/shellrc.tmpl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@ content readable.
5151
# different shells.
5252
PATH="$(
5353
IFS=:
54-
for path_dir in $(echo $PATH); do
54+
for path_dir in $(echo "$PATH"); do
5555
case "$path_dir" in
5656
$NIX_STORE/*) nix_path="${nix_path:+$nix_path:}${path_dir}" ;;
5757
*) non_nix_path="${non_nix_path:+$non_nix_path:}${path_dir}" ;;

nix/testdata/shellrc/basic/shellrc.golden

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,7 @@ zstyle ':completion:*:kill:*' command 'ps -u $USER -o pid,%cpu,tty,cputime,cmd'
6565
# different shells.
6666
PATH="$(
6767
IFS=:
68-
for path_dir in $(echo $PATH); do
68+
for path_dir in $(echo "$PATH"); do
6969
case "$path_dir" in
7070
$NIX_STORE/*) nix_path="${nix_path:+$nix_path:}${path_dir}" ;;
7171
*) non_nix_path="${non_nix_path:+$non_nix_path:}${path_dir}" ;;

nix/testdata/shellrc/nohook/shellrc.golden

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,7 @@ zstyle ':completion:*:kill:*' command 'ps -u $USER -o pid,%cpu,tty,cputime,cmd'
6565
# different shells.
6666
PATH="$(
6767
IFS=:
68-
for path_dir in $(echo $PATH); do
68+
for path_dir in $(echo "$PATH"); do
6969
case "$path_dir" in
7070
$NIX_STORE/*) nix_path="${nix_path:+$nix_path:}${path_dir}" ;;
7171
*) non_nix_path="${non_nix_path:+$non_nix_path:}${path_dir}" ;;

nix/testdata/shellrc/noshellrc/shellrc.golden

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@
2323
# different shells.
2424
PATH="$(
2525
IFS=:
26-
for path_dir in $(echo $PATH); do
26+
for path_dir in $(echo "$PATH"); do
2727
case "$path_dir" in
2828
$NIX_STORE/*) nix_path="${nix_path:+$nix_path:}${path_dir}" ;;
2929
*) non_nix_path="${non_nix_path:+$non_nix_path:}${path_dir}" ;;

0 commit comments

Comments
 (0)