I remember the moment environment variables finally clicked for me: I was debugging why a script worked on my machine but failed on a colleague’s, and it turned out a variable I assumed was globally set simply wasn’t there on his system. Environment variables are one of those foundational concepts that seem simple on the surface but have real depth once you start relying on them for configuration, automation, and scripting.
In this guide, I’ll cover what environment variables actually are, how they differ from regular shell variables, how to create and export them, and how to use them effectively in scripts and real projects.
What Is an Environment Variable?
An environment variable is a named value stored in the shell’s environment that can be accessed by the shell itself and by any programs or scripts it launches. Think of it as a piece of configuration data available system-wide (or session-wide) rather than something local to a single script.
Common examples you’ve probably already seen:
echo $HOME
echo $PATH
echo $USER
echo $SHELL
These are set automatically when your shell starts, and countless programs rely on them to behave correctly.
Environment Variables vs. Shell Variables
This distinction trips up a lot of beginners, so let’s be precise:
- Shell variables exist only within the current shell session and are not passed to child processes.
- Environment variables are shell variables that have been “exported,” making them available to any child process spawned from that shell.
Example:
MY_VAR="hello"
echo $MY_VAR # works, prints "hello"
bash -c 'echo $MY_VAR' # prints nothing, because it's not exported
Now with export:
export MY_VAR="hello"
bash -c 'echo $MY_VAR' # prints "hello"
The second command spawns a new Bash process, and because MY_VAR was exported, that child process inherits it.
Viewing Existing Environment Variables
To see all currently exported environment variables:
printenv
or:
env
To check a single variable:
printenv PATH
or simply:
echo $PATH
Setting a Variable for the Current Session
export EDITOR="nano"
Now any program that checks the EDITOR variable (like crontab -e or git commit) will open nano instead of whatever the system default is.
This setting only lasts for your current terminal session unless you make it permanent (covered below).
Setting a Variable Temporarily for a Single Command
Sometimes you only want a variable set for one specific command, without affecting the whole session:
MY_VAR="temporary" ./script.sh
This sets MY_VAR only in the environment of script.sh and doesn’t persist afterward or affect your interactive shell.
Making Environment Variables Permanent
To persist variables across sessions, add export statements to your shell configuration file, typically ~/.bashrc for interactive shells or ~/.bash_profile / ~/.profile for login shells.
nano ~/.bashrc
Add:
export EDITOR="vim"
export PATH="$HOME/scripts:$PATH"
export NODE_ENV="development"
Reload:
source ~/.bashrc
Why We Prepend to PATH Like That
Notice export PATH="$HOME/scripts:$PATH". This takes the existing PATH value and adds your custom scripts directory in front of it. Order matters here — directories listed earlier are searched first, so this ensures your custom scripts take priority over system binaries with the same name.
Common Built-In Environment Variables
| Variable | Purpose |
|---|---|
HOME | Path to the current user’s home directory |
PATH | List of directories searched for executable commands |
USER | Current logged-in username |
SHELL | Path to the user’s default shell |
PWD | Current working directory |
LANG | System language and locale settings |
EDITOR | Default text editor for command-line tools |
HISTSIZE | Number of commands kept in shell history |
Using Environment Variables in Scripts
Here’s a small script that demonstrates practical use:
#!/bin/bash
# Use an environment variable with a fallback default
LOG_DIR="${LOG_DIR:-/var/log/myapp}"
mkdir -p "$LOG_DIR"
echo "Logging to $LOG_DIR" >> "$LOG_DIR/app.log"
Let’s break this down:
${LOG_DIR:-/var/log/myapp}— this is parameter expansion with a default value. IfLOG_DIRis set in the environment, its value is used; otherwise,/var/log/myappis used instead.mkdir -pcreates the directory (and any missing parent directories) without erroring if it already exists.- The final line appends a log message to a file inside that directory.
This pattern is extremely common in real-world scripts because it lets the same script adapt to different environments (development, staging, production) just by changing an environment variable, without touching the script’s code.
Unsetting a Variable
unset MY_VAR
This removes the variable entirely from the current session, including from the environment if it was exported.
Real-World Use Cases
Configuration for applications: Node.js, Python, and many frameworks read variables like NODE_ENV, DATABASE_URL, or API_KEY from the environment rather than hardcoding them into source code — this keeps secrets out of your codebase.
Docker and containers: Environment variables are the standard way to configure containers:
docker run -e DATABASE_URL="postgres://localhost/mydb" myapp
CI/CD pipelines: Build systems like GitHub Actions or Jenkins inject secrets and configuration as environment variables so scripts can access credentials without exposing them in code.
Switching between environments:
export APP_ENV="staging"
./deploy.sh
The deploy script can then branch its behavior based on $APP_ENV.
Using .env Files
Many projects store environment variables in a .env file and load them at runtime rather than exporting them manually every session. Here’s a simple way to load one in Bash:
set -a
source .env
set +a
set -atells Bash to automatically export every variable defined from this point on.source .envloads the variable definitions from the file.set +aturns off automatic exporting again, so subsequent variables aren’t accidentally exported.
A typical .env file looks like:
DATABASE_URL=postgres://localhost/mydb
API_KEY=your_api_key_here
DEBUG=true
Best Practices
- Never hardcode secrets directly into scripts — use environment variables or
.envfiles instead. - Use uppercase names for environment variables by convention (
DATABASE_URL, notdatabase_url) to distinguish them from regular shell variables. - Provide sensible defaults using
${VAR:-default}so scripts don’t break in environments where a variable isn’t set. - Keep
.envfiles out of version control by adding them to.gitignore. - Document required environment variables in your project’s README so collaborators know what to set.
Security Considerations
- Environment variables are visible to any process running as the same user, and sometimes to other users via
/proc/[pid]/environon Linux if permissions aren’t locked down — avoid storing highly sensitive secrets this way in shared/multi-user systems. - Be careful with
envorprintenvoutput in shared terminal recordings or screenshots, since it can leak API keys or credentials. - Avoid passing secrets as command-line arguments (
myapp --api-key=xxxx), since these are visible in process lists (ps aux) to other users on the same system — environment variables are generally safer for this, though still not perfectly private. - Use dedicated secret managers (like Vault, AWS Secrets Manager, or Docker secrets) for production-grade secret handling instead of relying solely on plain environment variables.
Optimization Tips
- Avoid excessive appending to
PATHacross multiple config files, since a bloatedPATHslows down command lookups slightly and makes debugging harder. - Consolidate related environment variable exports into a single sourced file (like
~/.env_vars) rather than scattering them across.bashrc,.profile, and.bash_profile.
Troubleshooting Common Issues
Variable not available in a script but works in the terminal: The variable likely wasn’t exported. Use export VAR_NAME=value instead of just VAR_NAME=value.
Variable set in .bashrc doesn’t apply to cron jobs: Cron runs with a minimal environment and doesn’t source .bashrc. Set variables explicitly at the top of the crontab or inside the script itself.
Changes to .bashrc don’t seem to take effect: Make sure you’re editing the right file for your shell type (interactive vs. login shell), and remember to source it or open a new terminal.
PATH seems broken after editing it: Double-check you didn’t overwrite PATH entirely instead of appending to it — always include $PATH in the new value.
Frequently Asked Questions
What’s the difference between .bashrc and .bash_profile? .bashrc runs for interactive non-login shells (like opening a new terminal tab), while .bash_profile (or .profile) runs for login shells (like SSH sessions). Many setups source one from the other to keep behavior consistent.
Can environment variables be arrays or complex data? No, environment variables are always simple strings. For complex data, use a config file format like JSON or YAML and parse it in your script.
Do environment variables persist after a reboot? Only if they’re defined in a file that’s sourced at shell startup, like ~/.bashrc or /etc/environment.
How do I set a system-wide environment variable for all users? Add it to /etc/environment or /etc/profile.d/custom.sh, which apply to all users rather than just your account.
Common Mistakes to Avoid
- Forgetting
export, so a variable stays local to the shell and isn’t visible to scripts or subprocesses. - Committing
.envfiles with real secrets to version control. - Overwriting
PATHinstead of appending to it, which can break your entire terminal until you fix it. - Assuming environment variables set in one terminal automatically appear in another open terminal — they don’t, since each session has its own environment.
Summary
Environment variables are the backbone of configuration in shell scripting and application development. Once you understand the difference between shell variables and exported environment variables, and how to persist them in your configuration files, you can build flexible scripts and applications that adapt to different environments without hardcoding values. Combine this with .env files and sensible defaults, and you’ve got a solid, portable configuration strategy.
References
- GNU Bash Manual — Shell Variables: https://www.gnu.org/software/bash/manual/html_node/Shell-Variables.html
- GNU Bash Manual — Environment: https://www.gnu.org/software/bash/manual/html_node/Environment.html
