10

I modify my .bashrc frequently and then source it. However, when I have things like export PATH="~/bin:~/perl5/bin:$PATH" in my file, then the PATH environment variable grows every time I source the file.

For example, the first time .bashrc is sourced, the PATH variable consists of ~/bin:~/perl5/bin:/usr/bin:/bin.

The second time it consists of ~/bin:~/perl5/bin:~/bin:~/perl5/bin:/usr/bin:/bin.

The third time it consists of ~/bin:~/perl5/bin:~/bin:~/perl5/bin:~/bin:~/perl5/bin:/usr/bin:/bin.

Is there a simple way to make it only add anything that isn't already in the PATH?

Sam Halicke
  • 6,442

6 Answers6

13

Use the pathmunge() function available in most distro's /etc/profile:

pathmunge () {
if ! echo $PATH | /bin/egrep -q "(^|:)$1($|:)" ; then
   if [ "$2" = "after" ] ; then
      PATH=$PATH:$1
   else
      PATH=$1:$PATH
   fi
fi
}

edit: For zsh users, typeset -U <variable_name> will deduplicate path entries.

Sam Halicke
  • 6,442
3

I was having this issue so I used a combination of techniques listed on StackOverflow question. The following is what I used to dedupe the actual PATH variable that had already been set, since I didn't want to modify the base script.

    tmppath=(${PATH// /@})
    array=(${tmppath//:/ })
    for i in "${array[@]//@/ }"
    do
        if ! [[ $PATH_NEW =~ "$i" ]]; then
            PATH_NEW="${PATH_NEW}$i:";
        fi
    done;
    PATH="${PATH_NEW%:}"
    export PATH
    unset PATH_NEW

You could always optimize this a bit more, but I had extra code in my original to display what was happening to ensure that it was correctly setting the variables. The other thing to note is that I perform the following

  1. replace any SPACE character with an @ character
  2. split the array
  3. loop through the array
  4. replace any @ characters in the element string with a space

This is to ensure that I can handle directories with spaces in (Samba home directories with Active Directory usernames can have spaces!)

netniV
  • 200
1

Set your path explicitly.

Cakemox
  • 26,021
1

I can think of two different ways you could resolve this. The first one, is to start your .bashrc with a line that explicitly sets your base PATH, that way every time you source it, it is reset to the base prior to adding additional directories.

For example, add:

# Reset the PATH to prevent duplication and to make sure that we include
# everything we want.
export PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin

Alternately, you can check for an item before you add it to the path. To do that, you'd use something like:

if ! [[ $PATH =~ '~/perl5/bin' ]]
then
    PATH="~/perl5/bin:$PATH"
fi

The latter tends to get a little repetitive if you're adding a lot of entries, however, so I tend to stick with the former. If you wanted to use this and planned on adding a lot of entries, writing a bash function to handle it would be wise.

Note: The second option may only work as written in modern versions bash. The regular expression support is not a Bourne Shell (/bin/sh) feature, and may not exist in other shells. Also, the use of quotes may not be needed or may even cause problems on some newest versions of bash.

1

Only one string:

for i in $(echo $PATH|tr ":" "\n"|sort|uniq);do PATH_NEW="${PATH_NEW}$i:";done;PATH="${PATH_NEW%:}"
bindbn
  • 5,321
0

Here is my solution: PATH=$(echo -n $PATH | awk -v RS=: -v ORS=: '!x[$0]++' | sed "s/\(.*\).\{1\}/\1/")

A nice easy one liner that does not leave a trailing :