Tools: How To View and Update the Linux PATH Environment Variable (2026)
Source: DigitalOcean
By Piya Gehi, Caitlin Postal and Vinayak Baranwal The author selected the Wikimedia Foundation to receive a donation as part of the Write for DOnations program. 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 in this guide are validated on Ubuntu 22.04, Debian 12, and Rocky Linux 9. Commands are shown with expected output so you can compare your results and confirm each change is applied as intended. 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: If the same executable name exists in more than one directory, the first directory in this list wins. 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. echo $PATH is the fastest way to see what directories your shell searches when running a command: The output is a single string where each directory is separated by :. 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: Because printenv only prints exported variables, it cleanly distinguishes inherited environment from local shell variables. Scripts invoked with bash script.sh see a different PATH than your interactive shell because they do not source ~/.bashrc or ~/.bash_profile. This script prints the PATH a non-interactive bash process actually receives: To make a script’s PATH reliable regardless of how it is invoked, define PATH explicitly at the top of the script before any commands that depend on it: Alternatively, use the full absolute path to each command, for example /usr/bin/python3 instead of python3, so the script does not depend on PATH at all. 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: Prepending gives the new directory higher priority than system directories, which is useful when installing a newer version of a tool alongside the system version. Appending is safer for general use. Two commands confirm the change: echo $PATH shows the raw value, and which shows which binary the shell will actually run: This change applies to the current shell session only. When you close the terminal or start a new session, PATH resets to its default value. 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. For most interactive use on a desktop or server where you are the only user, ~/.bashrc (bash) or ~/.zshrc (zsh) is the correct file. Appending an export line to ~/.bashrc makes the PATH change available in every new interactive bash session. Open the file: Scroll to the bottom and append: Load the change without logging out: If /opt/myapp/bin appears in the output, the update is active for new commands in the current terminal. For zsh, the equivalent file is ~/.zshrc. Open it: Append the export line: Reload configuration: If your system opens login zsh shells and not interactive non-login shells, use ~/.zprofile instead. /etc/environment is parsed by PAM at login and is not a shell script, so shell syntax including $PATH variable expansion does not work in it. Read the current value before editing: Set the complete literal value, including existing directories and the new one: Do not use $PATH inside /etc/environment. Variable expansion is not supported in this file. Always write the complete directory list as a literal string. Changes in /etc/environment take effect after the next login, not after sourcing in the current shell. A new export line written to ~/.bashrc is not active until the file is sourced. Three methods reload the configuration without requiring a logout: exec bash starts a new interactive non-login shell, so it sources ~/.bashrc but not ~/.bash_profile. If your export line is in ~/.bash_profile only, use source ~/.bash_profile or open a new login session instead. which returns the first executable match for a command name across all PATH directories. After a PATH change, run it to confirm the shell resolves the command to the expected binary: The path returned is what the shell will execute next time you run the command, assuming nothing in the bash hash cache or alias table overrides it. type -a goes further than which. It reports every match across PATH, plus any aliases or shell functions with the same name. Use it when you need to verify that the correct version is first in resolution order: Run command -v for a POSIX-friendly single-path check: type is a shell builtin that reports aliases, functions, and all PATH matches, while which only checks PATH directories and returns the first match. Use type -a when you need to see every candidate to diagnose version conflicts. Running echo $PATH in the same session that applied export does not confirm persistence. It only confirms the current session state. Start a fresh shell process to verify the value comes from the startup file: If the new directory appears in the output, the startup file is correctly configured. If not, the export line is missing, in the wrong file, or sitting below an early-return guard. To remove /opt/myapp/bin from the current session: The trailing-colon pattern removes the directory when it is followed by a colon, covering start and middle positions. The leading-colon pattern removes it when it is preceded by a colon, covering middle and end positions. Running both together covers all positions. Run echo $PATH afterward to confirm removal. Sourcing ~/.bashrc multiple times in a session, for example after editing it, can append the same directory each time. Use this guard pattern to add the path only once: Wrapping colons around both $PATH and the target directory makes the pattern match a full entry, not a substring of a longer path. The export command updates PATH in the current shell process only. When that process exits, the change is gone. To confirm this is what happened, open a new terminal and run: If the directory you added is absent, the change was never written to a startup file. To trace exactly which files bash sources at login and in what order, run: The -l flag forces a login shell, -x enables trace output, and -v prints each line as it is read. Look for lines sourcing ~/.profile, ~/.bash_profile, or /etc/profile to confirm which file controls your login PATH. Then append your export line to the correct file and source it: Cause: PATH was updated in a subshell or script that exited, the wrong startup file was edited, or the current session has not yet loaded the change. Diagnostic: Confirm that the export line is present in the correct file and that source has been run or a new session has been opened. Cause: a directory containing an older version of the binary appears earlier in PATH than the directory containing the newer version. Diagnostic: Fix: prepend the directory containing the desired version using export PATH="/opt/myapp/bin:$PATH", or remove the directory containing the unwanted version using the sed pattern from Step 5. Cause: most desktop environments source ~/.profile and /etc/environment at login, not ~/.bashrc. A change in ~/.bashrc only affects terminal emulators that spawn interactive non-login shells. Fix: move the export line to ~/.profile, or add the directory to /etc/environment, then log out and back in. Cron runs with a minimal default PATH (typically /usr/bin:/bin). Commands available in your interactive shell may not be found. Set PATH at the top of the crontab file, before any job definitions. Open the crontab with crontab -e and add the PATH assignment first: The PATH line applies to every job defined below it in the same crontab file. If a script called by cron also relies on PATH-dependent commands, add the same PATH assignment at the top of that script. Use the ENV instruction in a Dockerfile to extend PATH for the image: ENV persists across all subsequent layers and into the running container, so every RUN, CMD, and ENTRYPOINT instruction sees the updated value. direnv loads and unloads environment variables automatically when you enter or leave a directory. Install it and add the shell hook to your startup file: On Rocky Linux, Fedora, or RHEL, replace the install line with sudo dnf install direnv. For zsh users on either distribution, replace bash with zsh in the hook command. The hook is required. Without it, direnv will not activate on directory change. Create a .envrc file in your project root: PATH reverts to its previous value when you cd out of the project. The following scenarios are the most common reasons a correctly configured PATH still does not work as expected in real deployments. 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. Check the current secure_path value: If /opt/myapp/bin is not listed, sudo myapp will fail regardless of your user PATH. To fix this without disabling secure_path, use the full path: Or add the directory to secure_path by editing /etc/sudoers with visudo: Change the secure_path line to include your directory: Always use visudo to edit /etc/sudoers. Editing the file directly with a text editor and introducing a syntax error will lock you out of sudo on that system. Running a command over SSH with ssh user@host 'mycommand' spawns a non-interactive, non-login shell. This shell sources neither ~/.bashrc nor ~/.bash_profile, so PATH changes in those files are invisible to it. To verify what PATH an SSH non-interactive shell sees: The output is a minimal system PATH set by sshd, with none of the directories from your interactive shell’s startup files. To make PATH available in this context, set it in ~/.bashrc and guard the block so it runs even in non-interactive shells: Check whether a [ -z "$PS1" ] && return guard exists near the top of your ~/.bashrc. This line causes bash to exit early for non-interactive shells, skipping everything below it. If the guard is present, move the export PATH line above it. If the guard is absent, the export line can stay at the bottom of the file. Alternatively, define PATH in ~/.profile and ensure ~/.bashrc sources it. After updating PATH, bash may still run the old binary because it caches the location of previously executed commands in a hash table. The cached entry overrides PATH lookup for that command name. Clear the cache after a PATH change: To check whether a specific command is cached and where it points: If the cached path is stale, hash -r clears all entries and forces a fresh PATH lookup on the next invocation. PATH is not the first place bash looks when you run a command. The resolution order is: This is why type -a is more useful than which for diagnosing unexpected behavior. If type python3 returns python3 is aliased to python, the alias takes precedence over any PATH entry. Remove or rename the alias to reach the binary. On macOS, the system runs /usr/libexec/path_helper at login to assemble PATH from two sources: /etc/paths (one directory per line) and files inside /etc/paths.d/. This runs before shell startup files, so directories listed there appear in PATH before anything in ~/.zshrc or ~/.bash_profile. To add a directory system-wide on macOS, create a file in /etc/paths.d/: The file takes effect on the next login. To verify: This section applies to macOS only. On Linux systems covered in this tutorial, /etc/paths and path_helper do not exist. When running applications as systemd services, the PATH from your user environment is not inherited. systemd starts services with a minimal environment. Commands installed in /opt/myapp/bin or similar locations will not be found unless PATH is set explicitly in the unit file. Set PATH in the [Service] block of the unit file: For multiple environment variables, use an EnvironmentFile: Where /etc/myapp/environment contains: After editing the unit file, reload the daemon and restart the service: PATH is a colon-separated list of directory paths that the shell searches, from left to right, when you type a command. When you run python3, the shell checks each directory in PATH until it finds an executable named python3, then runs it. Without PATH, every command would require typing its full absolute path. Run echo $PATH or printenv PATH in any terminal to display the current value. Both commands print the same colon-separated list of directories. To view one directory per line, run tr ':' '\n' <<< "$PATH". Running export PATH="$PATH:/new/dir" in the terminal applies the change to the current shell session only. When the session ends, PATH returns to the value set by the startup files. To persist the change, write the export line to ~/.bashrc (bash) or ~/.zshrc (zsh) and source the file. Edit ~/.bashrc for interactive bash sessions on a server or desktop where you log in as a single user. Use ~/.profile or ~/.bash_profile for login shell sessions, and /etc/environment when the change must apply to all users on the system. The table in Step 3 lists the scope and sourcing behavior of each file. export in a script does not affect the parent shell because the script runs in a subshell. Changes must be sourced (. script.sh or source script.sh) to apply to the current shell, or written to a startup file and loaded in a new session. Run echo $PATH immediately after export to confirm the change is visible in the current session. Open ~/.zshrc in a text editor, add export PATH="$PATH:/opt/myapp/bin" at the bottom, save the file, and run source ~/.zshrc. For login zsh shells, use ~/.zprofile instead of ~/.zshrc. Run which python3 to see the first match in PATH. Run type -a python3 to list every match across all PATH directories, which is useful when multiple versions are installed. Run command -v python3 for a POSIX-portable alternative to which. Run the following to remove /opt/myapp/bin from the current session: To make the removal permanent, delete or comment out the export line that added the directory from ~/.bashrc, ~/.zshrc, or whichever startup file contains it, then source the file. 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 the failures that occur in production environments including sudo stripping PATH, SSH non-interactive shells, stale binary caches, and systemd service isolation. You can now configure PATH for any user-installed tool, select the right startup file for your shell and login mode, and diagnose why a command is not found or resolves to the wrong binary. Next, continue with How To Read and Set Environmental and Shell Variables on Linux, An Introduction to the Linux Terminal, and An Introduction to Useful Bash Aliases and Functions. Thanks for learning with the DigitalOcean Community. Check out our offerings for compute, storage, networking, and managed databases. Learn more about our products Building future-ready infrastructure with Linux, Cloud, and DevOps. Full Stack Developer & System Administrator. Technical Writer @ DigitalOcean | GitHub Contributor | Passionate about Docker, PostgreSQL, and Open Source | Exploring NLP & AI-TensorFlow | Nailed over 50+ deployments across production environments. This textbox defaults to using Markdown to format your answer. You can type !ref in this text area to quickly search our full set of tutorials, documentation & marketplace offerings and insert the link! Please complete your information! Get paid to write technical tutorials and select a tech-focused charity to receive a matching donation. Full documentation for every DigitalOcean product. The Wave has everything you need to know about building a business, from raising funding to marketing your product. Stay up to date by signing up for DigitalOcean’s Infrastructure as a Newsletter. New accounts only. By submitting your email you agree to our Privacy Policy Scale up as you grow — whether you're running one virtual machine or ten thousand. From GPU-powered inference and Kubernetes to managed databases and storage, get everything you need to build, scale, and deploy intelligent applications.