I'm on Ubuntu 20.04 Focal, amd64
, Bash version 5.0.17(1)-release
.
In previous versions of Ubuntu, I've been able to write intuitive scripts to modify PATH
in a way in which multiple entries containing the same value aren't possible.
Source Code
My /etc/bash.bashrc
is what is provided by the distro, and so is my ~/.bashrc
, apart from the following lines:
~/.bashrc
# ... distro defaults above
export DEBUG=y
if [ -e "$HOME/.bash_functions" ]; then
source "$HOME/.bash_functions"
fi
if [ -d "$HOME/.bashrc.d" ]; then
find "$HOME/.bashrc.d" -maxdepth 1 -type f -name '*.sh' | sort -u | while read file ; do
source "$file"
done
fi
My ~/.bash_functions
includes my helper functions for modifying PATH
safely:
~/.bash_functions
#!/usr/bin/env bash
# log MESSAGE...
#
# Prints the provided arguments to standard error if the DEBUG variable is set to 'y'.
_log() {
test "$DEBUG" = "y" && ( echo -n 'DEBUG: ' && echo "$@" ) >&2
}
# path_exists PATH_ENTRY
#
# Returns true if the specified path entry exists within $PATH, and false otherwise.
_path_exists() {
path="$1" && shift
echo "$PATH" | tr ':' '\n' | while read path_entry ; do
test "$path" = "$path_entry" && return 0
done && return 0
return 1
}
# prepend_path PATH_ENTRY
#
# Pushes PATH_ENTRY to the beginning of $PATH, provided that PATH_ENTRY does not already exist in $PATH.
_prepend_path() {
path="$1" && shift
if ! _path_exists "$path" ; then
_log "prepend_path - Prepending $path to PATH..."
PATH="$path:$PATH"
export PATH
else
_log "prepend_path - $path is already present in PATH, nothing to do."
fi
}
# append_path PATH_ENTRY
#
# Pushes PATH_ENTRY to the end of $PATH, provided that PATH_ENTRY does not already exist in $PATH.
_append_path() {
path="$1" && shift
if ! _path_exists "$path" ; then
_log "append_path - Appending $path to PATH..."
PATH="$PATH:$path"
export PATH
else
_log "append_path - $path is already present in PATH, nothing to do."
fi
}
Finally, I have two files in ~/.bashrc.d
, one for adding Rust to the PATH
and one for nodenv
:
~/.bashrc.d/50-rust.sh
#!/usr/bin/env bash
if [ -d "$HOME/.cargo/bin" ]; then
_prepend_path "$HOME/.cargo/bin"
fi
~/.bashrc.d/50-node.sh
#!/usr/bin/env bash
NODENV_ROOT="$HOME/.nodenv"
if [ -d "$NODENV_ROOT/bin" ]; then
_prepend_path "$NODENV_ROOT/bin"
if ! _path_exists "$NODENV_ROOT/shims" ; then
eval "$(nodenv init -)"
fi
fi
Issue
Since I have exported DEBUG=y
in my ~/.bashrc
, I see output when I open a new shell:
DEBUG: prepend_path - Prepending /home/naftuli/.nodenv/bin to PATH...
DEBUG: prepend_path - Prepending /home/naftuli/.cargo/bin to PATH...
Clearly, the code is executing. If I emend my loop to print PATH
after sourcing each file, I see that it did, in fact, modify the environment. My modifications to ~/.bashrc
:
if [ -d "$HOME/.bashrc.d" ]; then
find "$HOME/.bashrc.d" -maxdepth 1 -type f -name '*.sh' | sort -u | while read file ; do
source "$file"
echo "PATH=$PATH"
done
fi
When I start a new shell, I observe:
DEBUG: prepend_path - Prepending /home/naftuli/.nodenv/bin to PATH...
PATH=/home/naftuli/.nodenv/shims:/home/naftuli/.nodenv/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games:/snap/bin
DEBUG: prepend_path - Prepending /home/naftuli/.cargo/bin to PATH...
PATH=/home/naftuli/.cargo/bin:/home/naftuli/.nodenv/shims:/home/naftuli/.nodenv/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games:/snap/bin
Again, clearly it is working, but somehow export
within those sourced files does not propagate all the way up to the actual shell environment, because after my shell starts:
$ echo $PATH
/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games:/snap/bin
In previous versions of Bash, this setup did work.
Question
Why aren't my export
s propagating upward to the shell on shell start? My code is running, and PATH
is modified, but outside of the loop, it's as if PATH
was never modified. What changes do I need to make in order to propagate variables upward to the shell?