keep duplicates out of $PATH on source

I have the following code that’s source-d by my .shellrc

PATH="${PATH}:${HOME}/perl5/bin"
PATH="${PATH}:${HOME}/.bin"
export PATH

but if I make changes to other code and then source this file, my path continues to get longer and longer with each source, each time appending these when they’re already there. What can I do to prevent this?

Answers:

Thank you for visiting the Q&A section on Magenaut. Please note that all the answers may not help you solve the issue immediately. So please treat them as advisements. If you found the post helpful (or not), leave a comment & I’ll get back to you as soon as possible.

Method 1

add_to_PATH () {
  for d; do
    d=$({ cd -- "$d" && { pwd -P || pwd; } } 2>/dev/null)  # canonicalize symbolic links
    if [ -z "$d" ]; then continue; fi  # skip nonexistent directory
    case ":$PATH:" in
      *":$d:"*) :;;
      *) PATH=$PATH:$d;;
    esac
  done
}
add_to_PATH ~/perl5/bin ~/.bin

The line for symbolic link canonicalization is optional. If you remove it, also remove the next line (if you want to keep nonexistent directories), or change it to

if ! [ -d "$d" ]; then continue; fi

Note that the symlink canonicalization method only guarantees unicity amongst directories that were added by this function. It also doesn’t handle edge cases like an NFS directory mounted on two locations or a Linux bind mount.

Method 2

You could put a test around the “append this directory to path” command which would check to see if foo is already in the path before adding it, but it wouldn’t buy you much.

First, the test itself would be costly compared to appending a duplicate element. Secondly, a redundant element later in the path has no effect upon what does get executed when you execute a given command because the first matching executable in the path will still be the one executed. Finally most shells cache prior path hits in a hash table so the second time you execute my_command the path isn’t even searched.

About the only thing that not appending redundant entries will get you is a prettier looking path, but most paths are pretty ugly to begin with. If this aesthetic goal is really important to you, tell us which shell you are using and I can conjure up a function to “append this to path only if it isn’t present” function.

Method 3

I use these functions that are sourced from an initialization script by fink on os x (so credit goes to the fink developers). They work great and I can re-source my .bash_profile whenever I want. Don’t ask me how they work… I just know they do 🙂

# define append_path and prepend_path to add directory paths, e.g. PATH, MANPATH
# add to end of path
append_path()
{
  if ! eval test -z ""${$1##*:$2:*}"" -o -z ""${$1%%*:$2}"" -o -z ""${$1##$2:*}"" -o -z ""${$1##$2}"" ; then
    eval "$1=$$1:$2"
  fi
}

# add to front of path
prepend_path()
{
  if ! eval test -z ""${$1##*:$2:*}"" -o -z ""${$1%%*:$2}"" -o -z ""${$1##$2:*}"" -o -z ""${$1##$2}"" ; then
    eval "$1=$2:$$1"
  fi
}

I can use them like so to append or prepend to $PATH or $MANPATH (they’ll work with any variable formatted like $PATH):

prepend_path PATH $macPortsDir/sbin
prepend_path MANPATH $macPortsDir/man

Method 4

One thing you could do is use an environment variable as a guard.
So set the env to __<your script>__path_added.

In your script, you can then just test if that has been set before adding the path in. A bit like you would a C header guard.


All methods was sourced from stackoverflow.com or stackexchange.com, is licensed under cc by-sa 2.5, cc by-sa 3.0 and cc by-sa 4.0

0 0 votes
Article Rating
Subscribe
Notify of
guest

0 Comments
Oldest
Newest Most Voted
Inline Feedbacks
View all comments
0
Would love your thoughts, please comment.x
()
x