views:

104

answers:

6

Guys I constantly need to attach new paths to the PATH environment variable in .bashrc, like below: export PATH=/usr/local/bin:$PATH Then to make it take effect, I always do 'source ~/.bashrc' or '. ~/.bashrc', while I found one shortcoming of doing so which make me uncomfortable.

If I keep doing so, the PATH will getting longer and longer with many duplicated entries, for example in the previous command, if I source it twice, the value of PATH will be PATH=/usr/local/bin:/usr/local/bin:/usr/local/bin:$PATH(<-the original path).

Is there a more decent way to attach new path to PATH in bashrc without making it ugly?

A: 

My solution is the one liner:

export PATH=`echo :<new path>:${PATH} | sed -e 's/\:/\n/g' | sort | uniq \
| awk 'BEGIN {ORS=":"} {print $0}'`

where sed replaces : with a newline, sort and uniq strip out any duplicates, and awk rebuilds the path. This does have the disadvantage that the order is not maintained, so if you want to have programs in one location to have precedence over ones in other locations, this will cause problems. I haven't implemented it, but I imagine you could use perl to maintain these in order while stripping out the duplicates.

rcollyer
Clever, but definitely mucks with precedence.
Will Hartung
+2  A: 

Another way is to check if OPATH isn't set. If it is, set it to PATH. This will be your original PATH.

if [ "$OPATH" == "" ];
then
    OPATH=$PATH
fi

PATH=~/bin:$OPATH

(Code is untested...)

Will Hartung
I tested the code and it works fine, it's clever to record the original thing in the first place, thanks.
Ripley
+1  A: 

If you're willing to entertain a change of shell, zsh has declare -U for this exact purpose: it'll automatically strip duplicates from an array while maintaining precedence. It also lets you use $path instead of $PATH.

% PATH=a:b:c:d:c:b:a
% echo $PATH
a:b:c:d:c:b:a
% declare -U PATH
% echo $PATH     

or, for improved readability, you can use the array form, which is kept synchronized automatically:

% path=(a b c d c b a)
% print $path
a b c d c b a
% print $PATH
a:b:c:d:c:b:a
% declare -U path
% print $path
a b c d
% print $PATH 
a:b:c:d
Nicholas Riley
+1  A: 

My approach is like rcollyer's, but more universal, deals with precedence and uses much more code.

function append () {
        local val
        eval val=\$$1
        if [[ x$val = x ]] ; then
                eval $1=$2
        else 
                eval $1="$val:$2"
        fi
}

function is_in() {
        local pattern
        pattern=":$1\$|^$1:|:$1:"
        echo $2 | egrep -q "$pattern" &&  return 0
        return 1
}

function append_if_absent() {
        local val
        eval val=\$$1   
        if ! is_in "$2" "$val" ; then
                append "$1" "$2"
        fi
}

export ROOTSYS=/usr/local/root

append_if_absent LD_LIBRARY_PATH $ROOTSYS/lib/root
append_if_absent PATH $ROOTSYS/bin
Basilevs
A: 

Here is what I have been using for a long time: I added a function called addpath() to .bash_profile, or .bashrc and then I can add a directory to the path knowing there will be no duplicate. For example:

addpath ~/bin
addpath ~/myproj/bin

Here is the source for addpath():

function addpath()
{

    if [ $# -eq 0 ]
    then
        echo "  Usage: addpath dir ..."
        return 1
    fi

    local p
    local dir
    local IFS=:
    local found

    for dir; do
        found=0
        for p in $PATH; do
            if [ "$p" = "$dir" ]; then
                found=1
            fi
        done

        if [ "_$found" = "_0" ]; then
            PATH=$PATH:$dir
        fi
    done
}
Hai Vu
A: 

From my .bashrc:

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

pathadd /usr/local/bin
pathadd ~/bin
...etc

Note that this adds directories to the end of the path; make the obvious change to add them to the beginning.

Gordon Davisson