DEV Community

Cover image for Appending environment variables to PATH the easy way (Linux)
Ivo Karaneshev
Ivo Karaneshev

Posted on

Appending environment variables to PATH the easy way (Linux)

Appending to PATH variable

In Linux, you have seen something like this

export SOME_ENV="..."
PATH="$SOME_ENV:$PATH"
Enter fullscreen mode Exit fullscreen mode

which append the env variable value to PATH.

It is really annoying to do this on the command line. So Linux guys suggest putting this in the ~/.bashrc file.

Excellent.

But it can become a real mess quickly because there is all other important stuff in .bashrc.

I don't feel it right to have everything bash-related bumped into one file.

Quick fix

So I decided to move all of my configured env variables to a separate file instead of messing things up in .bashrc.
I called it .bash_envs and placed it in my home directory (where .bashrc is located).

I also separated bash functions and aliases into
.bash_functions and .bash_aliases respectively with the same idea in mind

In order to work properly, .bashrc needs to know about and execute the .bash_envs file.

So to accomplish this I wrote the following code in .bashrc:

#Exported env definitions.
if [ -f ~/.bash_envs  ]; then
    . ~/.bash_envs
fi
Enter fullscreen mode Exit fullscreen mode

Also .bash_envs file needs to be executable so I typed a quick:

chmod +x .bash_envs
Enter fullscreen mode Exit fullscreen mode

And the content of .bash_envs includes something like this:

#!/bin/bash
...

# java
export JAVA_HOME="/usr/lib/jvm/java-21-openjdk-amd64"
PATH="$JAVA_HOME/bin:$PATH"

# pnpm 
export PNPM_HOME="$HOME/.local/share/pnpm"
PATH="$PNPM_HOME:$PATH"

...
Enter fullscreen mode Exit fullscreen mode

Perfect.

But to be honest, it is really annoying to type PATH=... every time an env variable is exported.

So I came up with a better idea.

The improvement

If I could somehow store the required paths in something like an array, then join the items with a colon symbol (:), and finally append it to PATH?

Wait a second, there are arrays in bash and I could do something like this:

path_envs=(
    "$JAVA_HOME/bin"
    "$PNPM_HOME"
    ...
)
Enter fullscreen mode Exit fullscreen mode

But actually, I think it's better to append every item immediately after the env variable export, because otherwise you can easily miss an item and you have to manually check if everything is as you expect.

Then the code should become something like this:

path_envs=() # defining an empty array

# java
export JAVA_HOME="/usr/lib/jvm/java-21-openjdk-amd64"
path_envs+=("$JAVA_HOME/bin") # appending java path to array

# pnpm
export PNPM_HOME="$HOME/.local/share/pnpm"
path_envs+=("$PNPM_HOME") # appending pnpm path to array
Enter fullscreen mode Exit fullscreen mode

So far so good. But how to actually join items with a colon :?

There are plenty of solutions but the easiest one I found was this:

PATH="$( IFS=":" ; echo "${path_envs[*]}" ):$PATH"
Enter fullscreen mode Exit fullscreen mode

Don't worry I will explain step by step the above code snippet.

Explanation

$( Dollar Single Parentheses )

So the $( ... ) parentheses part means that everything inside will be executed in a subshell and the result will be returned as a string.

For example:

echo "Hello, my name is $( whoami )"
# => "Hello, my name is ivo"
Enter fullscreen mode Exit fullscreen mode

Internal Field Separator (IFS)

The content inside the paratheses is more interesting. The first part (before the semicolon ;) sets the special variable Internal Field Separator (IFS) with a value of colon :.
This in short means it treats : as a separator between words/items in a string instead of whitespace which is the default.

More detailed information with examples about IFS can be found here.

The second part just prints the path_envs array of its items separated by IFS. One notable detail is that path_envs[*] is used instead of path_env[@] for expanding the array, because [*] returns a single shell-word with all of the elements of the array separated by IFS (as we need), while [@] leads to each element of the array being treated as a separate shell-word.

As a result, the subshell should return something like:

/usr/lib/jvm/java-21-openjdk-amd64:/home/ivo/.local/share/pnpm...
Enter fullscreen mode Exit fullscreen mode

Final result

All of that is finally concatenated with the old value of PATH and simply that was the work we should do.

In the end, my .bash_envs file looked something like this:

#!/bin/bash

path_envs=()

# java
export JAVA_HOME="/usr/lib/jvm/java-21-openjdk-amd64"
path_envs+=("$JAVA_HOME/bin")

# pnpm
export PNPM_HOME="$HOME/.local/share/pnpm"
path_envs+=("$PNPM_HOME")

# adding path_envs to PATH
PATH="$( IFS=":" ; echo "${path_envs[*]}" ):$PATH"
Enter fullscreen mode Exit fullscreen mode

Top comments (2)

Collapse
 
functionguyy profile image
Gilbert Adikankwu

Hello Ivo,

Nice article! Thanks for sharing. I have a question though: it seems to me that this line PATH="$( IFS=":" ; echo "${path_envs[*]}" ):$PATH" inside the .bash_envs file will execute everytime a shell starts up, will that not cause duplicates in $PATH

Collapse
 
ivokara profile image
Ivo Karaneshev

Thank you! Actually yes, if you restart the shell (e.g. in the opened terminal window you type exec bash) or you source again your .bashrc (source ~/.bashrc) - then yes, there would be duplications. But this doesn't seem to be a problem for bash to execute things properly.

In the other hand, if you open new shell (terminal window or tab) .bashrc will be sourced only once initially so there will be no duplications.

This shows that every modification you do on $PATH will be individual for every shell. The only common (and initial) thing between them would be what you have appended to $PATH in the .bashrc or .bash_envs file.