A more comfortable way to edit a long $PATH?

I want to add, in ~/.bashrc, a few directories to my $PATH.

My $PATH is quite long so it's a bit hard to see what directories it contains and in what order.

I know I can modify my ~/.bashrc to be:

PATH=$PATH:/some/dir
PATH=$PATH:/another/dir:/yet/another
PATH=$PATH:/and/another
...

it'd make it easier to read. But I was wondering if during the last years Bash has acquired some syntax that makes specifying a long PATH easier. E.g., I'm fantasizing about a syntax similar to:

PATH=:((
  /some/dir
  /another/dir
  /yet/another
  /and/another
  ...
))

I know such syntax is invalid. I was wondering if there's something as easy. Is there?


I use a set of convenience functions for prepending or appending a path to a variable. The functions come in the distribution tarball for Bash in a contrib file called "pathfuncs".

  • add_path will add the entry to the end of the PATH variable
  • pre_path will add the entry to the beginning of the PATH variable
  • del_path will remove the entry from the PATH variable, wherever it is

If you specify a variable as the second argument, it will use that instead of PATH.

For convenience, here they are:

# is $1 missing from $2 (or PATH) ?
no_path() {
    eval "case :\$${2-PATH}: in *:$1:*) return 1;; *) return 0;; esac"
}
# if $1 exists and is not in path, append it
add_path () {
  [ -d ${1:-.} ] && no_path $* && eval ${2:-PATH}="\$${2:-PATH}:$1"
}
# if $1 exists and is not in path, prepend it
pre_path () {
  [ -d ${1:-.} ] && no_path $* && eval ${2:-PATH}="$1:\$${2:-PATH}"
}
# if $1 is in path, remove it
del_path () {
  no_path $* || eval ${2:-PATH}=`eval echo :'$'${2:-PATH}: |
    sed -e "s;:$1:;:;g" -e "s;^:;;" -e "s;:\$;;"`
}

If you add those to your bash startup file, you can add to your PATH like this:

pre_path $HOME/bin
add_path /sbin
add_path /usr/sbin

Or specify a different variable:

pre_path $HOME/man MANPATH
pre_path $HOME/share/man MANPATH
add_path /usr/local/man MANPATH
add_path /usr/share/man MANPATH

I use this method in my rc files putting the pre_paths first and the add_paths second. It makes all of my path changes easy to understand at a glance. Another benefit is that the lines are short enough that I can add a trailing comment on a line if necessary.

And since these are functions, you can use them interactively from the command line, such as by saying add_path $(pwd) to add the current directory to the path.


OK, I figured out the following solution, which I think is elegant (as far as shell syntax go). It uses Bash's array syntax and also a neat way to join elements:

paths=(
  /some/dir
  /another/dir
  '/another/dir with spaces in it'
  /yet/another
  /and/another
  /end
)
paths_joined=$( IFS=: ; echo "${paths[*]}" )

PATH=$paths_joined:$PATH

ALERT!

It turns out that this solution has a problem: Unlike the solutions of @terdon and @Starfish, it doesn't first check whether the paths are already in PATH. So, since I want to put this code in ~/.bashrc (and not in ~/.profile), duplicate paths will creep into PATH. So don't use this solution (unless you put it in ~/.profile (or, better, ~/.bash_profile as it has Bash specific syntax)).


I use the function below in my ~/.bashrc. It is something I got from a sysadmin in my old lab but I don't think he wrote them. Just add these lines to your ~/.profile or ~/.bashrc:

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

This has various advantages:

  • Adding new directories to the $PATH is trivial: pathmunge /foo/bar;
  • It avoids duplicated entries;
  • You can choose whether to add a new entry to the beginning (pathmunge /foo/bar or the end (pathmunge /foo/bar after) of the $PATH.

Your shell's initialization file would then contain something like:

pathmunge /some/dir
pathmunge /another/dir
pathmunge '/another/dir with spaces in it'
pathmunge /yet/another
pathmunge /and/another
pathmunge /end

I want to add, in ~/.bashrc, a few directories to my $PATH.

I use the following in Cygwin. It should work in other versions of bash. You can remove the unset PATH to build on your current PATH (if you do this you may have to figure out how to add the : separators correctly).

Note:

  • I once had this functionality in a bash function but lost it after a disk crash.

In my .bash_profile:

# Build up the path using the directories in ~/.path_elements
unset PATH
while read line; do 
  PATH="${PATH}$line"; 
done < ~/.path_elements

...

# Add current directory to path
export PATH=".:${PATH}"

In ~/.path_elements:

/home/DavidPostill/bin:
/usr/local/bin:
/usr/bin:
/c/Windows/system32:
/c/Windows