After starting a bash terminal, I noticed that the PATH variable contains duplicate entries. My terminal starts a login shell, so ~/.bash_profile
is sourced, followed by ~/.profile
and ~/.bashrc
. Only in ~/.profile
do I create the paths entries which are duplicated.
To be pedantic, this is the order in which the files that SHOULD be sourced are being sourced:
Sourced /etc/profile
Sourced /etc/bash.bashrc
Sourced .bash_profile
Sourced .profile
Sourced .bashrc
Before anyone marks this as a duplicate of "PATH variable contains duplicates", keep reading.
At first I thought this had to do with ~/.profile
being sourced twice, so I had the file write to a log file whenever it was sourced, and surprisingly it only logged one entry, which tells me that it was only sourced once. Even more surprising is the fact that when I comment out the entries which were in ~/.profile
, the entries still appear in the PATH
variable. This has led me to three conclusions, one of which was quickly ruled out:
- Bash ignores valid bash comments and still executes the commented code
- There is a script which reads the
~/.profile
and ignores any code that prints an output (the log file for example) - There is another copy of my
~/.profile
which is being sourced elsewhere
The first one, I quickly concluded not to be the case due to some quick testing. The second and third options are where I need help with.
How do I gather a list of scripts which are executed when my terminal starts up? I used echo
in the files that I checked to know if they are sourced by bash, but I need to find a conclusive method which traces the execution up the point when the terminal is ready for me to start typing into it.
If the above is not possible, then can anyone suggest where else I can look to see which scripts are being run.
Future reference
This is the script I now use for adding to my path:
function add_to_path() {
for path in ${2//:/ }; do
if ! [[ "${!1}" =~ "${path%/}" ]]; then # ignore last /
new_path="$path:${!1#:}"
export "$1"="${new_path%:}" # remove trailing :
fi
done
}
I use it like this:
add_to_path 'PATH' "/some/path/bin"
The script checks if the path already exists in the variable before prepending it.
For zsh users, you can use this equivalent:
# prepends the given path(s) to the supplied PATH variable
# ex. add_to_path 'PATH' "$(go env GOPATH)/bin"
function add_to_path() {
# (P)1 path is expanded from $1
# ##: Removes leading :
local -x pth="${(P)1##:}"
# (s.:.) splits the given variable at :
for p in ${(s.:.)2}; do
# %%/ Remove trailing /
# :P Behaves similar to realpath(3)
local p="${${p%%/}:P}"
if [[ ! "$pth" =~ "$p" ]]; then
pth="$p:$pth"
fi
done
export "$1"="${pth%%:}"
}
Edit 28/8/2018
One more thing I found I could do with this script is to also fix the path. So at the start of my .bashrc
file, I do something like this:
_temp_path="$PATH"
PATH='/usr/local/sbin:/usr/local/bin:/usr/bin:/usr/sbin:/sbin:/bin:/usr/games:/usr/local/games:/snap/bin'
add_to_path 'PATH' "$_temp_path"
unset _temp_path
It is up to you what the PATH
should start with. Examine PATH
first to decide.
~/.profile
if~/.bash_profile
does not exist...~/.profile
and~/.bashrc
from~/.bash_profile