Skip to content

SessionVault, a Chrome extension we built and maintain for a client, is on the Chrome Web Store.We built SessionVault, now on the Chrome Web Store.Read how we built it

ThynkDone
← All posts

Linux Basics

How To View and Update the Linux PATH Environment Variable

Learn how to view and update the Linux PATH environment variable permanently or temporarily across bash and zsh, with verification, troubleshooting, and production gotchas.

ThynkDone5 min read

A terminal window displaying Linux command-line output, illustrating PATH environment variable configuration

Introduction

The Linux PATH environment variable is a colon-separated list of directories the shell searches when you run a command. Each time you enter a command name such as python3, grep, or myapp, the shell checks directories in PATH from left to right and runs the first executable match it finds.

In this tutorial, you will view PATH, change it temporarily and permanently across bash and zsh, verify changes, remove entries, and troubleshoot common failures. Examples are validated on Ubuntu 22.04, Debian 12, and Rocky Linux 9.


Key Takeaways

  • PATH is a colon-separated list of directories the shell searches left to right when resolving a command.
  • echo $PATH and printenv PATH both display the current value.
  • export PATH=$PATH:/new/dir changes PATH for the current session only.
  • Permanent changes go in ~/.bashrc for bash, ~/.zshrc for zsh, and /etc/environment for system-wide values.
  • Order matters: directories listed earlier in PATH take precedence over later ones.

Prerequisites

  • A Linux system running bash or zsh (Ubuntu 22.04, Debian 12, or Rocky Linux 9).
  • A non-root user with sudo privileges.
  • Basic familiarity with environment variables.

Understanding How the Linux PATH Variable Works

What PATH Contains and How the Shell Uses It

PATH stores absolute directory paths separated by colons. When you run a command without a full path, your shell inspects each directory in order and executes the first matching file with execute permissions.

To inspect your PATH as one directory per line, run:

tr ':' '\n' <<< "$PATH"

If the same executable name exists in more than one directory, the first directory in this list wins.

How PATH Is Inherited by Processes and Subshells

Child processes inherit exported environment variables from the parent process. A shell variable exists only in the current shell until you mark it with export. PATH follows this same rule.

MYVAR="hello"
bash -c 'echo $MYVAR'   # prints nothing

export MYVAR="hello"
bash -c 'echo $MYVAR'   # prints: hello

Step 1 — Viewing the Current PATH Variable

Using echo to Display PATH

echo $PATH is the fastest way to see what directories your shell searches:

echo $PATH

Using printenv to Display PATH

printenv PATH reads directly from the process environment rather than through shell expansion, making it the more reliable option in scripts and non-interactive contexts:

printenv PATH

Reading PATH in a Script Context

Scripts invoked with bash script.sh see a different PATH than your interactive shell because they do not source ~/.bashrc. To make a script's PATH reliable, define it explicitly at the top:

#!/usr/bin/env bash
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
echo "Script PATH: $PATH"

Step 2 — Adding a Directory to PATH for the Current Session

export marks a variable for inheritance by child processes and updates it immediately in the current shell. To add a directory to PATH for the current session, prepend it for higher priority or append it for lower priority:

# Prepend: /opt/myapp/bin is checked before all existing directories
export PATH="/opt/myapp/bin:$PATH"

# Append: /opt/myapp/bin is checked after all existing directories
export PATH="$PATH:/opt/myapp/bin"

Step 3 — Adding a Directory to PATH Permanently

Choosing the Right Configuration File

The correct startup file depends on your shell and whether the session is login or interactive non-login. Choose the file that matches how your shell is launched:

FileScopeWhen SourcedShells
~/.bashrcPer userEvery interactive non-login bash shellbash
~/.bash_profilePer userLogin bash shellsbash
~/.profilePer userLogin shells when no ~/.bash_profile existsbash, sh, dash
~/.zshrcPer userEvery interactive zsh shellzsh
~/.zprofilePer userLogin zsh shellszsh
/etc/environmentSystem-wideRead by PAM at login; not a shell scriptAll
/etc/profileSystem-wideLogin shellsbash, sh
/etc/profile.d/*.shSystem-wideSourced by /etc/profilebash, sh

Editing ~/.bashrc for Bash Users

nano ~/.bashrc

Scroll to the bottom and append:

export PATH="$PATH:/opt/myapp/bin"

Load the change without logging out:

source ~/.bashrc

Editing /etc/environment for System-Wide Changes

sudo nano /etc/environment

# Set the complete literal value:
PATH="/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/opt/myapp/bin"

Changes in /etc/environment take effect after the next login, not after sourcing in the current shell.


Step 4 — Verifying That PATH Changes Are Working

Using which, type, and command -v

Three commands help you verify which binary the shell will actually run:

CommandWhat It DoesBest For
which python3Returns first PATH matchQuick single-path check
type -a python3Lists all matches including aliases and functionsDiagnosing version conflicts
command -v python3POSIX-portable single-path checkScripts and portability
which python3
# /usr/bin/python3

type -a python3
# python3 is /usr/bin/python3
# python3 is /usr/local/bin/python3

command -v python3
# /usr/bin/python3

Confirming Persistence with a New Shell

Running echo $PATH in the same session that applied export does not confirm persistence. Start a fresh shell to verify:

exec bash
echo $PATH

Step 5 — Removing or Deduplicating Entries from PATH

To remove /opt/myapp/bin from the current session:

PATH=$(echo "$PATH" | sed -e 's|/opt/myapp/bin:||g' -e 's|:/opt/myapp/bin||g')
export PATH

To prevent duplicate entries when sourcing config files multiple times, use a guard pattern:

case ":$PATH:" in
  *":/opt/myapp/bin:"*) ;;
  *) export PATH="$PATH:/opt/myapp/bin" ;;
esac

Troubleshooting Common PATH Problems

PATH Change Not Persisting After Logout

The export command updates PATH in the current shell process only. To trace which files bash sources at login:

bash -lxv 2>&1 | head -50

Wrong Version of a Binary Being Resolved

A directory containing an older version appears earlier in PATH than the newer one. Diagnose with type -a, then fix by prepending the correct directory:

type -a python3
export PATH="/opt/myapp/bin:$PATH"

PATH Changes Not Applying to GUI Applications


Managing PATH in Non-Standard Environments

PATH in Shell Scripts and Cron Jobs

PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin

# Run myapp every hour
0 * * * * /opt/myapp/bin/myapp >> /var/log/myapp.log 2>&1

PATH in Docker Containers and Dockerfiles

ENV PATH="/opt/myapp/bin:${PATH}"

ENV persists across all subsequent layers and into the running container, so every RUN, CMD, and ENTRYPOINT instruction sees the updated value.


Common PATH Gotchas in Production

sudo Ignores Your PATH

Running a command with sudo often fails with "command not found" even when the command works without sudo. The cause is secure_path in /etc/sudoers, which replaces PATH entirely when sudo is invoked.

sudo grep secure_path /etc/sudoers
# Defaults    secure_path="/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"

# To fix, edit with visudo and add your directory:
sudo visudo

SSH Non-Interactive Shells Do Not Source ~/.bashrc

Running a command over SSH with ssh user@host 'mycommand' spawns a non-interactive, non-login shell that sources neither ~/.bashrc nor ~/.bash_profile. Check whether a [ -z "$PS1" ] && return guard exists near the top of your ~/.bashrc. If it does, move your export PATH line above it.

Bash Caches Binary Locations

After updating PATH, bash may still run the old binary because it caches command locations in a hash table. Clear the cache:

hash -r

Command Resolution Precedence

PATH is not the first place bash looks when you run a command. The full resolution order is:

PriorityTypeExample
1 (highest)Aliasesalias python3='python3.11'
2Shell functionsfunction myapp() { ... }
3Shell builtinscd, echo, type
4Hashed locations (binary cache)Previously run commands
5 (lowest)PATH directory scan/usr/bin/python3

PATH in systemd Service Units

[Service]
Environment="PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/opt/myapp/bin"
ExecStart=/opt/myapp/bin/myapp
sudo systemctl daemon-reload
sudo systemctl restart myapp

Conclusion

This tutorial walked through the full lifecycle of PATH management on Linux: reading the current value, making temporary and permanent changes across bash and zsh, choosing the right startup file for your session type, verifying that the correct binary is resolved, removing and deduplicating entries, and diagnosing failures in production environments including sudo stripping PATH, SSH non-interactive shells, stale binary caches, and systemd service isolation.

Need a hand with this?

We build and run this kind of thing for clients every day. Tell us what you are working on.