Environment Variables, PATH & .bashrc
When you type ls, how does Bash know which program to run? When a web app reads APP_ENV=production, where does that value come from? And how do people get shortcuts like ll in their terminal? The answers are environment variables, the PATH, and the shell's start-up files.
In this lesson you will create and export variables, understand and change PATH, and customise Bash with .bashrc, aliases and small functions so that the changes survive a restart.
Shell variables
A variable is a name that holds a value. Create one with name=value, with no spaces around =, and read it with $name:
city=Pune
echo "$city"
echo "Orders from ${city}: 3"Pune Orders from Pune: 3
The braces in ${city} mark exactly where the name ends, which matters when text follows it directly. Variable names are case-sensitive; by convention, your own variables use lower case and environment variables use UPPER_CASE.
Environment variables and export
A plain variable belongs only to the current shell. Programs you start from that shell are child processes, and they do not see it unless you export it. Exported variables form the environment that every child process inherits.
bash -c 'echo "child sees: [$city]"'
export city
bash -c 'echo "child sees: [$city]"'
APP_ENV=production bash -c 'echo "APP_ENV is $APP_ENV"'
echo "after: [$APP_ENV]"child sees: [] child sees: [Pune] APP_ENV is production after: []
bash -c starts a new child shell. Before export, it saw nothing; after, it saw Pune. The third line shows a useful form: NAME=value command sets a variable only for that one command, and the variable is gone afterwards.
Linux sets many environment variables for you. printenv lists them all, and printenv NAME prints one:
| Variable | Holds |
|---|---|
HOME | Your home directory, /home/priya |
USER | Your user name |
SHELL | Your login shell, /bin/bash |
PWD | The current directory |
PATH | Folders searched for commands |
LANG | Language and character set, such as en_IN.UTF-8 |
EDITOR | Your preferred editor for tools like crontab -e |
PATH: how commands are found
When you type a command name without a /, Bash looks for a program with that name in each folder listed in PATH, from left to right, and runs the first one it finds.
echo "$PATH"
command -v python3
type -a ls/home/priya/.local/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games:/snap/bin /usr/bin/python3 ls is aliased to `ls --color=auto' ls is /usr/bin/ls ls is /bin/ls
Folders are separated by colons. command -v (or which) shows which file would run. type -a shows every match in order; ls appears twice only because /bin is a link to /usr/bin. Because /usr/local/bin comes before /usr/bin, a program you install there by hand wins over the distribution's version with the same name.
Suppose Priya writes a script called greet and keeps it in ~/bin:
greet
PATH="$HOME/bin:$PATH"
greetgreet: command not found Hello from greet, priya
The first try fails because ~/bin is not in PATH. Adding it to the front fixes that for the current shell. To run a program that is not in PATH at all, give its path: ./greet for one in the current folder. The current folder is deliberately not in PATH, so a malicious file named ls in a downloaded folder cannot trick you into running it.
Making changes permanent: start-up files
Everything above disappears when you close the terminal. To keep it, put it in a file that Bash reads when it starts:
| File | When it is read | Use it for |
|---|---|---|
~/.bashrc | Every new interactive shell (each terminal window or tab) | Aliases, functions, prompt, EDITOR |
~/.profile | Once, at login (SSH login, desktop login) | PATH changes and environment variables |
/etc/environment | At login, for every user | System-wide variables (simple NAME=value lines only) |
/etc/profile.d/*.sh | At login, for every user | System-wide scripts from packages or admins |
On Ubuntu, ~/.profile already adds ~/bin and ~/.local/bin to PATH when those folders exist, and it also loads ~/.bashrc. After editing a file, either open a new terminal or load it into the current shell with source:
nano ~/.bashrc
source ~/.bashrcsource (or its short form .) runs the file in the current shell, so its variables and aliases take effect immediately. Running the file as a normal program would change only a child shell, which then exits.
Aliases and functions
An alias is a short name for a longer command. Ubuntu's default .bashrc already defines a few, such as ll for ls -alF. Add your own at the end of ~/.bashrc:
alias ll='ls -alF'
alias update='sudo apt update && sudo apt upgrade'
alias ports='sudo ss -tulpn'
alias rm='rm -I'
mkcd() { mkdir -p "$1" && cd "$1"; }
export EDITOR=nanoThe rm='rm -I' alias adds a safety question before large deletes. Aliases cannot take arguments in the middle, so for anything smarter write a function: mkcd projects/new-app creates the folder and moves into it, using $1 for the first argument. You will learn functions properly in Functions, Arguments & Exit Codes. To bypass an alias for one run, put a backslash in front: \rm file.
Advanced: a custom prompt
The prompt is controlled by the variable PS1. Special codes insert information: \u user, \h hostname, \w current directory, \$ ($ or #). Here is a prompt that also shows the time:
PS1='[\t] \u@\h:\w\$ '[13:42:10] priya@laptop:~/projects$
Put the line in ~/.bashrc to keep it. Colour codes can be added too, but keep the prompt short and readable.
Common mistakes
- Spaces around
=.city = Puneruns a command calledcity. Writecity=Pune. - Forgetting
export. Your script or app cannot see a variable that was never exported. - Overwriting
PATH. Always append or prepend with:$PATH. - Editing
.bashrcand expecting the open terminal to change. Runsource ~/.bashrcor open a new terminal. - Putting secrets in
.bashrc. Anything there is easy to leak (screenshots, backups, dotfile repos). Keep secrets in protected files read only by the app.
Key points
name=valuecreates a variable (no spaces);$nameor${name}reads it.exportputs a variable into the environment so child processes inherit it;NAME=value cmdsets it for one command.PATHis a colon-separated list of folders searched left to right;command -vshows which program runs.~/.bashrcruns for every interactive shell and suits aliases and functions;~/.profileruns at login.source ~/.bashrcapplies changes to the current shell immediately.
Exercise
Make these changes permanent for your user: add an alias myip that runs ip -brief address, add a function backup that copies a file to the same name with .bak added, add the folder ~/scripts to the front of PATH, and set EDITOR to nano. Then load the changes and test backup notes/todo.txt.
Show solution
Add these lines to the end of ~/.bashrc:
alias myip='ip -brief address'
backup() { cp -v "$1" "$1.bak"; }
export PATH="$HOME/scripts:$PATH"
export EDITOR=nanoThen:
source ~/.bashrc
backup notes/todo.txt'notes/todo.txt' -> 'notes/todo.txt.bak'
The function uses quotes around "$1" so file names with spaces work. Strictly, PATH changes belong in ~/.profile, but putting them in ~/.bashrc is common for a single user; just avoid adding the same folder many times by sourcing repeatedly in one session.
Quick Quiz
1. What does export do?
Exported variables become part of the environment that child processes inherit.
2. You have two programs called tool, in /usr/local/bin and /usr/bin. Which runs?
Bash searches PATH from left to right and runs the first match.
3. Which line correctly adds ~/bin to PATH?
You must keep the old value; replacing PATH breaks every other command.
4. After adding an alias to ~/.bashrc, how do you use it in the terminal that is already open?
source runs the file in the current shell; bash file would only change a child shell.
Learn Linux with a mentor
Free lessons get you started. Our Ethical Hacking & Cyber Security training adds real projects and code reviews from working developers.
- Project-based learning
- Mentor code reviews
- Free counselling call
