You’re probably wrestling with where to put your custom shell aliases and environment variables, and if you put them in .bashrc, why they don’t show up in new terminal windows, or if you put them in .bash_profile, why they show up everywhere and you get duplicates. The fundamental issue is how Bash starts up and which files it reads under what conditions.

Let’s see it in action. Open a new terminal window.

echo "Hello from .bashrc" >> ~/.bashrc
source ~/.bashrc
echo $MY_VAR_BASHRC

You’ll see "Hello from .bashrc" printed. Now, open another terminal window.

echo $MY_VAR_BASHRC

It’s empty. This is because .bashrc is only sourced for interactive, non-login shells. The first terminal window you opened after adding the line was interactive, but the second one wasn’t a login shell.

Now let’s try .bash_profile.

echo "export MY_VAR_BASH_PROFILE='Hello from .bash_profile'" >> ~/.bash_profile
source ~/.bash_profile
echo $MY_VAR_BASH_PROFILE

You’ll see "Hello from .bash_profile". Now, open a new terminal window.

echo $MY_VAR_BASH_PROFILE

It’s there again! This is because .bash_profile (or .bash_login, or .profile if .bash_profile doesn’t exist) is sourced for interactive login shells. When you log into a system (via SSH, or sometimes the graphical login screen), Bash reads these files. New terminal windows you open from within your graphical session often start as interactive non-login shells, hence why they don’t pick up .bash_profile by default.

The common confusion arises because most people want their aliases and environment variables to be available in every terminal window they open, whether it’s a fresh login or a new tab in an existing terminal. This is where the interaction between .bash_profile and .bashrc becomes crucial.

Here’s the mental model:

  1. Login Shells: When you first log in (e.g., ssh user@server, or sometimes the very first terminal you open after booting into a GUI), Bash reads:

    • /etc/profile (system-wide configurations)
    • Then, it looks for ~/.bash_profile, ~/.bash_login, or ~/.profile (in that order). It reads the first one it finds. This is where you typically set up your PATH and other environment variables that should be available globally for your session.
  2. Interactive Non-Login Shells: When you open a new terminal window or tab after you’ve already logged in (e.g., opening a new GNOME Terminal window), Bash starts as an interactive non-login shell. It reads:

    • /etc/bash.bashrc (system-wide configurations for interactive shells)
    • ~/.bashrc (your user-specific configurations for interactive shells). This is where aliases, shell functions, and interactive prompt customizations usually go.

The trick is that .bash_profile is often configured to source .bashrc. This way, anything you put in .bashrc becomes available in both login shells (because .bash_profile sources it) and interactive non-login shells (because they read .bashrc directly).

A standard ~/.bash_profile often looks like this:

# ~/.bash_profile

# Get the aliases and functions
if [ -f ~/.bashrc ]; then
    . ~/.bashrc
fi

# User specific environment and startup programs

# Make sure PATH is set
export PATH=$PATH:$HOME/bin

# Other environment variables
export EDITOR=vim

In this common setup, the line if [ -f ~/.bashrc ]; then . ~/.bashrc; fi is the magic. It checks if ~/.bashrc exists, and if it does, it executes (sources) it. The . is a shorthand for source.

So, the rule of thumb:

  • Put environment variables (like PATH, JAVA_HOME, API_KEY) and commands that should run once when you log in into ~/.bash_profile.
  • Put aliases, shell functions, and interactive prompt customizations into ~/.bashrc.
  • Ensure your ~/.bash_profile sources your ~/.bashrc.

This ensures that everything is loaded for new terminal windows while still maintaining the distinction between login and non-login shells.

The one thing most people don’t realize is that if you’re running Bash in a non-interactive mode (like in a script that isn’t explicitly invoked with bash -i), neither .bash_profile nor .bashrc is sourced by default. You have to explicitly source them within your script if you need those definitions.

If you’ve put your configurations in the right files but they’re still not taking effect, the most common culprit is that you forgot to source the file or open a new terminal. The command source ~/.bash_profile or source ~/.bashrc (depending on where you put your setting) will apply the changes to your current shell session immediately without needing to open a new one.

The next confusion you’ll likely run into is how sh and other shells interact with these files, and the role of ~/.profile versus ~/.bash_profile.

Want structured learning?

Take the full Bash course →