Tell me more ×
Super User is a question and answer site for computer enthusiasts and power users. It's 100% free, no registration required.

Has anybody written a bash function to add a directory to $PATH only if it's not already there?

I typically add to PATH using something like:

export PATH=/usr/local/mysql/bin:$PATH

If I construct my PATH in .bash_profile, then it's not read unless the session I'm in is a login session -- which isn't always true. If I construct my PATH in .bashrc, then it runs with each subshell. So if I launch a Terminal window and then run screen and then run a shell script, I get:

$ echo $PATH
/usr/local/mysql/bin:/usr/local/mysql/bin:/usr/local/mysql/bin:....

I'm going to try building a bash function called add_to_path() which only adds the directory if it's not there. But, if anybody has already written (or found) such a thing, I won't spend the time on it.

share|improve this question
See stackoverflow.com/questions/273909/… for some infrastructure that can help. – dmckee Sep 11 '09 at 17:49

9 Answers

up vote 24 down vote accepted

From my .bashrc:

pathadd() {
    if [ -d "$1" ] && [[ ":$PATH:" != *":$1:"* ]]; then
        PATH="${PATH:+"$PATH:"}$1"
    fi
}

Note that PATH should already be marked as exported, so reexporting is not needed. Also, this adds the new directory to the end of the path; to put at the beginning, use PATH="$1:$PATH" at the obvious place. Finally, this checks whether the directory exists & is a directory before adding it, which you may not care about.

share|improve this answer
3  
I care. – Dennis Williamson Sep 12 '09 at 3:30
2  
This gives an invalid PATH if the original PATH is empty. – bukzor Jul 13 '11 at 18:03
2  
@bukzor I patched it to handle an empty PATH. – Gordon Davisson Nov 11 '12 at 22:46
This doesn't work correctly if $1 is the final entry in the path before calling pathadd. Because $1 doesn't exist within $PATH with a : before and a : after it, it's not found and $1 is appended to the path. It will only add the $1 to the path a second time, though. – Neil Apr 5 at 21:38
@Neil: It does work, because it compares with ":$PATH:" instead of just "$PATH" – Gordon Davisson Apr 5 at 21:43
show 1 more comment

Here's something from my answer to this question combined with the structure of Doug Harris' function. It uses Bash regular expressions:

add_to_path ()
{
    if [[ "$PATH" =~ (^|:)"${1}"(:|$) ]]
    then
        return 0
    fi
    export PATH=${1}:$PATH
}
share|improve this answer
This worked for me only using $1 instead of ${1} – Andrei Sep 5 '12 at 19:25
@Andrei: Yes, the braces are unnecessary in this instance. I'm not sure why I included them. – Dennis Williamson Sep 5 '12 at 19:31

A simple alias like this one below should do the trick:

alias checkInPath="echo $PATH | tr ':' '\n' | grep -x -c "

All it does is split the path on the : character and compare each component against the argument you pass in. grep checks for a complete line match, and prints out the count.

Sample usage:

$ checkInPath "/usr/local"
1
$ checkInPath "/usr/local/sbin"
1
$ checkInPath "/usr/local/sbin2"
0
$ checkInPath "/usr/local/" > /dev/null && echo "Yes" || echo "No"
No
$ checkInPath "/usr/local/bin" > /dev/null && echo "Yes" || echo "No"
Yes
$ checkInPath "/usr/local/sbin" > /dev/null && echo "Yes" || echo "No"
Yes
$ checkInPath "/usr/local/sbin2" > /dev/null && echo "Yes" || echo "No"
No

Replace the echo command with addToPath or some similar alias/function.

share|improve this answer
Using "grep -x" is probably faster than the loop I put in my bash function. – Doug Harris Sep 11 '09 at 18:04

Put this in the comments to the selected answer, but comments don't seem to support PRE formatting, so adding the answer here:

@gordon-davisson I'm not a huge fan of unnecessary quoting & concatenation. Assuming you are using a bash version >= 3, you can instead use bash's built in regexs and do:

pathadd() {
    if [ -d "$1" ] && [[ ! $PATH =~ (^|:)$1(:|$) ]]; then
        PATH+=:$1
    fi
}

This does correctly handle cases where there are spaces in the directory or the PATH. There is some question as to whether bash's built in regex engine is slow enough that this might net be less efficient than the string concatenation and interpolation that your version does, but somehow it just feels more aesthetically clean to me.

share|improve this answer

Here's what I whipped up:

add_to_path ()
{
    path_list=`echo $PATH | tr ':' ' '`
    new_dir=$1
    for d in $path_list
    do
    	if [ $d == $new_dir ]
    	then
    		return 0
    	fi
    done
    export PATH=$new_dir:$PATH
}

Now in .bashrc I have:

add_to_path /usr/local/mysql/bin
share|improve this answer

See How to keep from duplicating path variable in csh? on StackOverflow for one set of answers to this question.

share|improve this answer
function __path_add(){  
if [ -d "$1" ] ; then  
    local D=":${PATH}:";   
    [ "${D/:$1:/:}" == "$D" ] && PATH="$PATH:$1";  
    PATH="${PATH/#:/}";  
    export PATH="${PATH/%:/}";  
fi  
}  
share|improve this answer
idempotent_path_prepend ()
{
    PATH=${PATH//":$1"/} #delete any instances in the middle or at the end
    PATH=${PATH//"$1:"/} #delete any instances at the beginning
    export PATH="$1:$PATH" #prepend to beginning
}

When you need $HOME/bin to appear exactly once at the beginning of your $PATH and nowhere else, accept no substitutes.

share|improve this answer

Here's mine (I believe it was written years ago by Oscar, the sysadmin of my old lab, all credit to him), its been around in my bashrc for ages. It has the added benefit of allowing you to prepend or append the new directory as desired:

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

Usage:

$ echo $PATH
/bin/:/usr/local/bin/:/usr/bin
$ pathmunge /bin/
$ echo $PATH
/bin/:/usr/local/bin/:/usr/bin
$ pathmunge /sbin/ after
$ echo $PATH
/bin/:/usr/local/bin/:/usr/bin:/sbin/
share|improve this answer

Your Answer

 
discard

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.