How to Use Environment Variables in Bash

How to Use Environment Variables in Bash

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

VariablePurpose
HOMEPath to the current user’s home directory
PATHList of directories searched for executable commands
USERCurrent logged-in username
SHELLPath to the user’s default shell
PWDCurrent working directory
LANGSystem language and locale settings
EDITORDefault text editor for command-line tools
HISTSIZENumber 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. If LOG_DIR is set in the environment, its value is used; otherwise, /var/log/myapp is used instead.
  • mkdir -p creates 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 -a tells Bash to automatically export every variable defined from this point on.
  • source .env loads the variable definitions from the file.
  • set +a turns 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 .env files instead.
  • Use uppercase names for environment variables by convention (DATABASE_URL, not database_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 .env files 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]/environ on Linux if permissions aren’t locked down — avoid storing highly sensitive secrets this way in shared/multi-user systems.
  • Be careful with env or printenv output 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 PATH across multiple config files, since a bloated PATH slows 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 .env files with real secrets to version control.
  • Overwriting PATH instead 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

Total
2
Shares

Leave a Reply

Previous Post
How to Archive and Compress Files in Bash

How to Archive and Compress Files in Bash

Next Post
How to Customize Your Bash Prompt

How to Customize Your Bash Prompt

Related Posts