Linux Environment Variables: export and .bashrc
• Shell variables exist only in the current shell, while environment variables (created with `export`) are inherited by child processes—understanding this distinction prevents configuration headaches.
Key Insights
• Shell variables exist only in the current shell, while environment variables (created with export) are inherited by child processes—understanding this distinction prevents configuration headaches.
• The .bashrc file executes for interactive non-login shells, making it the right place for aliases and custom environment variables in most terminal sessions.
• Always append to PATH using export PATH="$PATH:/new/path" rather than overwriting it—the order matters, and losing system paths breaks your system.
What Are Environment Variables?
Environment variables are key-value pairs that configure how your Linux system and applications behave. They’re fundamental to Unix-like systems, controlling everything from where the shell looks for executables to how your terminal displays colors.
Every process on your system has access to a set of environment variables. When you launch a program, it inherits these variables from its parent process, allowing system-wide configuration to propagate throughout your session.
Common environment variables you’ll encounter include:
# Display your PATH (where the system looks for executables)
echo $PATH
# Output: /usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin
# Show your home directory
echo $HOME
# Output: /home/username
# Display current username
echo $USER
# Output: username
To see all environment variables in your current session:
# List all environment variables
env
# Or use printenv for specific variables
printenv USER
printenv PATH
The env command dumps everything, which can be overwhelming. Use printenv with a variable name when you know what you’re looking for.
Viewing and Setting Variables in the Current Session
Here’s where beginners stumble: not all variables are environment variables. When you create a variable in bash, it starts as a shell variable—local to that shell instance only.
# Create a shell variable (NOT an environment variable)
MY_VAR="hello world"
# This works in the current shell
echo $MY_VAR
# Output: hello world
This seems fine until you try to use it in a child process:
# Set a shell variable
DATABASE_URL="postgresql://localhost/mydb"
# Try to use it in a child process
bash -c 'echo $DATABASE_URL'
# Output: (empty - the variable isn't inherited)
The child bash process doesn’t see DATABASE_URL because it’s only a shell variable. This is a critical distinction that affects how you configure tools and scripts.
The export Command
The export command promotes a shell variable to an environment variable, making it available to all child processes spawned from that shell.
# Create and export in one step
export DATABASE_URL="postgresql://localhost/mydb"
# Now child processes can see it
bash -c 'echo $DATABASE_URL'
# Output: postgresql://localhost/mydb
You can also export existing shell variables:
# Create shell variable first
API_KEY="abc123xyz"
# Then export it
export API_KEY
# Verify it's now an environment variable
bash -c 'echo $API_KEY'
# Output: abc123xyz
The most common use of export is modifying PATH:
# Add a directory to PATH
export PATH="$PATH:/usr/local/go/bin"
# Add multiple directories
export PATH="$PATH:$HOME/bin:$HOME/.local/bin"
Notice the pattern: "$PATH:/new/path". The quotes prevent word splitting, and we append to the existing PATH rather than replacing it. The order matters—directories are searched left to right, so put custom paths where you want them in the search order.
Here’s a script demonstrating variable inheritance:
#!/bin/bash
# parent.sh
SHELL_VAR="I'm local"
export ENV_VAR="I'm exported"
# Spawn a child process
bash -c '
echo "Shell variable: $SHELL_VAR"
echo "Environment variable: $ENV_VAR"
'
Running this shows:
Shell variable:
Environment variable: I'm exported
Temporary vs. Permanent Variables
Variables set with export last only for your current session. Close the terminal, and they’re gone:
# Set a variable
export EDITOR="vim"
# It works now
echo $EDITOR
# Output: vim
# Close terminal and reopen
echo $EDITOR
# Output: (empty or default value)
For variables you need consistently, you must add them to shell configuration files that execute automatically when you start a new session.
Understanding .bashrc and Shell Startup Files
The .bashrc file in your home directory is a bash script that runs whenever you start an interactive non-login shell—which is most terminal sessions in modern Linux environments.
View your current .bashrc:
cat ~/.bashrc
A typical .bashrc contains:
# ~/.bashrc
# If not running interactively, don't do anything
case $- in
*i*) ;;
*) return;;
esac
# History settings
HISTCONTROL=ignoreboth
HISTSIZE=1000
# Enable color support
if [ -x /usr/bin/dircolors ]; then
alias ls='ls --color=auto'
alias grep='grep --color=auto'
fi
# Custom aliases
alias ll='ls -alF'
alias la='ls -A'
# Add custom bin directory to PATH
export PATH="$PATH:$HOME/bin"
To add your own environment variables, edit .bashrc:
# Open in your editor
nano ~/.bashrc
# Add at the end:
export EDITOR="vim"
export VISUAL="vim"
export GOPATH="$HOME/go"
export PATH="$PATH:$GOPATH/bin"
After editing, reload .bashrc without closing your terminal:
source ~/.bashrc
# Or use the shorthand
. ~/.bashrc
Note on .bash_profile vs .bashrc: Login shells (like SSH sessions) read .bash_profile or .profile, not .bashrc. Most .bash_profile files source .bashrc anyway, so putting variables in .bashrc usually works everywhere. If you need login-shell-specific configuration, use .bash_profile.
Best Practices and Common Patterns
Naming conventions: Use UPPERCASE for environment variables and lowercase for shell-only variables. This is convention, not requirement, but it aids readability:
# Environment variables - UPPERCASE
export DATABASE_URL="postgresql://localhost/mydb"
export API_TIMEOUT="30"
# Shell variables - lowercase
local_temp="/tmp/myapp"
Organize your .bashrc: Group related configurations with comments:
# ============================================
# Development Environment
# ============================================
export GOPATH="$HOME/go"
export GOBIN="$GOPATH/bin"
export PATH="$PATH:$GOBIN"
# ============================================
# Editor Configuration
# ============================================
export EDITOR="vim"
export VISUAL="vim"
# ============================================
# Application-Specific
# ============================================
export DATABASE_URL="postgresql://localhost/mydb"
export REDIS_URL="redis://localhost:6379"
Conditional PATH additions: Only add directories that exist:
# Add directory only if it exists
[ -d "$HOME/bin" ] && export PATH="$PATH:$HOME/bin"
# Add multiple directories conditionally
for dir in "$HOME/.local/bin" "$HOME/go/bin" "/usr/local/go/bin"; do
[ -d "$dir" ] && export PATH="$PATH:$dir"
done
Avoid duplicates in PATH:
# Function to add to PATH only if not already present
add_to_path() {
if [ -d "$1" ] && [[ ":$PATH:" != *":$1:"* ]]; then
export PATH="$PATH:$1"
fi
}
add_to_path "$HOME/bin"
add_to_path "$HOME/.local/bin"
Troubleshooting Common Issues
Variables not persisting: If your exported variables disappear, check which file you edited. For graphical terminal emulators, .bashrc is usually correct. For SSH sessions, you might need .bash_profile.
# Debug which files are being sourced
bash -xl
# This shows each command as it executes during shell startup
PATH order problems: If the wrong version of a program runs, check PATH order:
# See which executable will run
which python
# Output: /usr/bin/python
# Check PATH order
echo $PATH | tr ':' '\n'
# Shows each directory on a separate line
If system directories come before your custom ones, programs there take precedence. Put custom paths earlier in PATH:
# Put custom directory first
export PATH="$HOME/.local/bin:$PATH"
Quoting issues: Always quote variable values containing spaces:
# Wrong - breaks with spaces
export MY_PATH=/path/with spaces/here
# Right
export MY_PATH="/path/with spaces/here"
# When using variables in PATH, quote the entire expression
export PATH="$PATH:$HOME/my tools/bin"
Variable not expanding: Use double quotes for expansion, single quotes for literals:
# This doesn't expand $HOME
export PATH='$PATH:$HOME/bin'
echo $PATH
# Output includes literal "$HOME"
# This expands correctly
export PATH="$PATH:$HOME/bin"
echo $PATH
# Output: /usr/bin:...:home/username/bin
Debugging undefined variables: Check if a variable is set and what its value is:
# Shows if variable exists and its value
echo "PATH is: ${PATH:-not set}"
# More verbose debugging
set | grep MY_VAR
env | grep MY_VAR
Understanding environment variables and .bashrc is fundamental to Linux system administration and development. Master these concepts, and you’ll configure systems efficiently and troubleshoot issues quickly. The patterns here—conditional PATH additions, organized configuration files, proper quoting—will serve you throughout your Linux journey.