Tuesday, May 12, 2020

Always Modify PATH and Other Variables Conditionally

You MUST use conditional statements while modifying the PATH environment variable (and others) in your shell startup files. I will use bash for this example, but the reasoning is the same for all shells.

A typical modification to the PATH in ~/.bash_profile looks something like this:

export PATH=$PATH:/usr/local/Cellar/node/14.2.0/bin

The problem is that if the file is "sourced" twice in the shell, your path ends up looking like this, with the same directory there twice. It works, but it slows things down:

$ source ~/.bash_profile
$ echo $PATH
/usr/bin:/usr/local/Cellar/node/14.2.0/bin:/usr/local/Cellar/node/14.2.0/bin
$ 

So don't source it then! Unfortunately sourcing has to happen in all kinds of situations, including:
  • Something was added and the current shell needs the update
  • A new shell at the command with -l or --login will source ~/.bash_profile over the existing environment
  • ~/.bashrc is usually read in the original login shell, and then sourced again in each sub-shell which is over the existing environment
We have a simple fix, use a conditional statement to add something to PATH only if it is not already there:

dir=/usr/local/Cellar/node/14.2.0/bin

if echo $PATH | grep -v -q node
then
    export PATH=$PATH:$dir
fi 

Notice that the check is not for the directory itself, what if the directory was changed. The check is to look for something that is unique to this directory in the PATH.

Unfortunately the fix only handles new PATH entries, not a change to existing values. We can fix that by using sed to replace what is there when something already exists:

dir=/usr/local/Cellar/node/14.2.0/bin

if echo $PATH | grep -v -q node
then
    export PATH=$PATH:$dir
else
    export PATH=$(echo $PATH | sed -E "s|(.*):?([^:]*node[^:]*):?(.*$)|\1:$DIR:\3|" | sed -E "s|::|:|g" | sed -E "s|:$||")
fi 

There are three sed commands in the pipeline; the second two strip out empty and trailing colons if they end up in the PATH.

None of this is absolutely perfect, for example if the unique word we are looking for changes and we need to replace the directory it really will not work. But, what we have done is really not complicated and covers most of the scenarios. It is just a matter of consistently using conditional statements in the shell startup files to avoid duplication in environment variables like PATH.

No comments:

Post a Comment