Server Fault is a question and answer site for system and network administrators. It's 100% free, no registration required.

Sign up
Here's how it works:
  1. Anybody can ask a question
  2. Anybody can answer
  3. The best answers are voted up and rise to the top

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?

share|improve this question
up vote 10 down vote accepted

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

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

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

share|improve this answer
Thanks! It's not in /etc/profile on Debian Lenny, so I include it in my .bashrc. – Christopher Bottoms Oct 19 '10 at 16:15
Usage: pathmunge /some/path will place /some/path at the beginning of $PATH and pathmunge /some/path after will place /some/path at the end of $PATH – Christopher Bottoms Oct 19 '10 at 16:18
Cleaner way of doing the check to see if the hew directory exists in the current path, in modern bash shells: if ! [[ $PATH =~ (^|:)$1($|:) ]] ; then. – Christopher Cashell Oct 28 '10 at 16:38

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[@]//@/ }"
        if ! [[ $PATH_NEW =~ "$i" ]]; then
    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!)

share|improve this answer

Set your path explicitly.

share|improve this answer
Thanks. I tried setting the path explicitly, but I have a .bashrc file that I use in multiple environments, so the exact default PATH is not always the same. – Christopher Bottoms Oct 19 '10 at 16:26

Only one string:

for i in $(echo $PATH|tr ":" "\n"|sort|uniq);do PATH_NEW="${PATH_NEW}$i:";done;PATH="${PATH_NEW%:}"
share|improve this answer
Thanks. One difficultly this would cause me is that it alphabetically (or ASCIIbetically) reorders the contents of $PATH. I like to put specific directories at the beginning of $PATH (like /home/username) so that my personal copies of executables are run instead of the built-in defaults. – Christopher Bottoms Oct 19 '10 at 16:32

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' ]]

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.

share|improve this answer
Thanks. I tried setting the path explicitly, but I have a .bashrc file that I use in multiple environments, so the exact default PATH is not always the same. – Christopher Bottoms Oct 19 '10 at 16:20
You can still handle that by checking in the script to see what your local hostname is, and then setting your absolute path appropriately for that server. – Christopher Cashell Oct 19 '10 at 16:26
True. I might eventually do that. Thanks! – Christopher Bottoms Oct 19 '10 at 16:35
Just a typo: the ! is missing in the if. Also, it's surprisingly (to me) easy to make this into a function which loops over space-separated arguments, so you can just say: add_to_PATH ~/perl5/bin ~/.bin (I guess the case statement used there is more portable, but your if is clearer to me). EDIT weird coincidence that that question also adds perl5! – 13ren Dec 26 '12 at 3:33
@13ren : Thanks for the note. I also just realized I used single quotes in the PATH set line, instead of double-quotes like I should have. As written, it would have replaced your existing path with the variable name. Oops! Apparently that was a bad entry for me for typos; both are fixed now. – Christopher Cashell Dec 26 '12 at 22:32

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 :

share|improve this answer

Your Answer


By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.