What Is the Linux Shell Profile and Bashrc File?

.bashrc and shell profile files (.bash_profile, .profile, /etc/profile) are configuration files that Bash reads automatically at startup to set up your shell environment — aliases, functions, PATH additions, environment variables, and prompt customization. ~/.bashrc runs for every new interactive terminal window, while ~/.bash_profile or ~/.profile runs only once at login. On most Linux desktop systems, ~/.profile sources ~/.bashrc, so in practice most customization goes in ~/.bashrc.

The Files That Shape Every Terminal Session

Every time you open a terminal on Linux, before you see the prompt, before you type your first command, Bash reads and executes a sequence of configuration files. These files set up your PATH, define your aliases, configure your prompt’s appearance, set environment variables, and can run any command you want automatically. Understanding this startup sequence — which files run when, and why there are multiple files rather than just one — is essential to customizing your shell environment correctly and troubleshooting why a setting is not taking effect.

The confusion around .bashrc, .bash_profile, .profile, and /etc/profile is one of the most common points of friction for people learning Linux. Why are there multiple files? Why does a setting in one file work in some situations but not others? Why do tutorials sometimes say to edit .bashrc and sometimes .bash_profile for what seems like the same purpose?

This article resolves that confusion by explaining precisely when each file runs, why the distinction exists (rooted in decades-old Unix design decisions), what belongs in each file, and how to structure your own shell configuration correctly.

The Fundamental Distinction: Login Shells vs. Interactive Non-Login Shells

Everything about shell startup files hinges on one distinction: whether a shell is a login shell or a non-login shell, and whether it is interactive or non-interactive.

Login Shell

A login shell is started when you first authenticate into a system:

  • Logging in via SSH: ssh user@server
  • Logging in at a text console (Ctrl+Alt+F2, then entering username/password)
  • Explicitly starting one: bash --login or bash -l
  • Some (but not all) graphical desktop login sequences

A login shell represents the beginning of a session — the shell that exists from the moment you log in until you log out.

Non-Login Shell

A non-login shell is started within an existing session — you are already logged in, and you start an additional shell:

  • Opening a new terminal window (GNOME Terminal, Konsole, etc.) while already logged into your desktop
  • Running bash from within an existing shell
  • Most terminal emulator windows on a desktop system

Interactive vs. Non-Interactive

A shell is interactive if it presents a prompt and reads commands from you directly. A shell is non-interactive if it is running a script or executing a single command without a prompt — for example, when cron runs a script, or when you run bash script.sh.

The Four Combinations

Login Non-Login
Interactive SSH session, console login New terminal window in desktop
Non-Interactive Rare (some remote command execution) Scripts run with bash script.sh

Different files are read for each combination, which is the source of all the confusion.

Which Files Run When

Login + Interactive Shells

When you start a login shell, Bash reads (in order, stopping at the first one found):

  1. /etc/profile — system-wide, always read first regardless of what follows
  2. Then the first of: ~/.bash_profile, ~/.bash_login, or ~/.profile

Bash reads only ONE of these three — whichever is found first in that priority order. If ~/.bash_profile exists, ~/.bash_login and ~/.profile are ignored entirely for this shell.

Non-Login + Interactive Shells

When you open a new terminal window in your existing desktop session, Bash reads:

  1. /etc/bash.bashrc — system-wide (on Debian/Ubuntu; other distributions may vary)
  2. ~/.bashrc — your personal interactive shell configuration

Non-Interactive Shells (Scripts)

When running a script (bash script.sh or ./script.sh with a bash shebang), Bash normally reads none of these files by default — scripts run in a clean environment unless the script explicitly sources a configuration file, or the BASH_ENV environment variable is set to point to a file to read.

Why the Split? Historical Context

This division dates back to the original Bourne shell design decades ago and reflects a reasonable underlying logic, even if it causes modern confusion:

Login-time setup should happen exactly once per session: setting your PATH, setting environment variables like EDITOR or LANG, running one-time login messages or checks. Doing this in every new terminal window would be wasteful and could cause problems if the login script does something session-specific (like starting an SSH agent).

Interactive shell setup — aliases, shell functions, prompt customization, shell options — needs to be present in every interactive shell, including every new terminal window you open, because these are shell-specific behaviors that apply per-shell-instance, not per-session.

The intended design: environment variables and PATH go in the login files (read once); aliases, functions, and prompt settings go in .bashrc (read for every interactive shell). In practice, this clean separation gets muddied because most desktop Linux configurations have the login file simply load .bashrc, collapsing the distinction for most everyday purposes.

The Ubuntu/Debian Default Setup

On Ubuntu and Debian, the default ~/.profile file contains logic that sources ~/.bashrc:

# ~/.profile (default Ubuntu content, abbreviated)
# if running bash
if [ -n "$BASH_VERSION" ]; then
    # include .bashrc if it exists
    if [ -f "$HOME/.bashrc" ]; then
        . "$HOME/.bashrc"
    fi
fi

# set PATH so it includes user's private bin if it exists
if [ -d "$HOME/bin" ] ; then
    PATH="$HOME/bin:$PATH"
fi

if [ -d "$HOME/.local/bin" ] ; then
    PATH="$HOME/.local/bin:$PATH"
fi

This means: on Ubuntu/Debian, when you log in (login shell), ~/.profile runs, which in turn sources ~/.bashrc. So both files’ contents end up loaded. When you open a new terminal (non-login shell), only ~/.bashrc runs directly.

The practical consequence: on Ubuntu/Debian systems, putting your customization in ~/.bashrc works correctly whether the shell is a login shell or not, because ~/.profile loads it in the login case anyway. This is why most Ubuntu users only ever touch ~/.bashrc and never think about ~/.profile or ~/.bash_profile.

What Goes Where: Practical Guidelines

Content for ~/.bashrc (Interactive Shell Settings)

# Aliases
alias ll='ls -la'
alias gs='git status'
alias ..='cd ..'

# Shell functions
extract() {
    tar -xzf "$1"
}

# Prompt customization
export PS1='\u@\h:\w\$ '

# Shell options
shopt -s histappend      # Append to history file, don't overwrite
shopt -s checkwinsize    # Update terminal size after each command

# History settings
HISTSIZE=10000
HISTFILESIZE=20000
HISTCONTROL=ignoredups:erasedups

# Enable programmable completion
if [ -f /etc/bash_completion ]; then
    . /etc/bash_completion
fi

Content for ~/.profile or ~/.bash_profile (Login/Environment Settings)

# PATH additions (needed once per session)
export PATH="$HOME/.local/bin:$PATH"
export PATH="$HOME/.cargo/bin:$PATH"

# Environment variables that should exist for the whole session
export EDITOR=nano
export LANG=en_US.UTF-8
export LESS='-R'

# Load .bashrc for interactive use
if [ -n "$BASH_VERSION" ]; then
    if [ -f "$HOME/.bashrc" ]; then
        . "$HOME/.bashrc"
    fi
fi

Why This Division Matters Less Than You Might Think

In practice, on modern Ubuntu/Debian desktop systems, since ~/.profile sources ~/.bashrc, you can put almost everything in ~/.bashrc and it will work correctly in nearly every situation you will personally encounter as a desktop user. The strict separation matters more for:

  • System administrators managing servers where SSH login shells are the primary access method
  • Script authors who need to understand what environment a script will have
  • Anyone troubleshooting why a setting works in one context but not another

/etc/profile and /etc/bash.bashrc: System-Wide Configuration

Beyond your personal configuration files, system-wide files apply to all users on the machine.

/etc/profile

Read by all login shells, for all users, before their personal profile files:

$ cat /etc/profile
# /etc/profile: system-wide .profile file for the Bourne shell (sh(1))
# and Bourne compatible shells (bash(1), ksh(1), ash(1), ...).

if [ "$(id -u)" -eq 0 ]; then
  PS1='# '
else
  PS1='$ '
fi

if [ -d /etc/profile.d ]; then
  for i in /etc/profile.d/*.sh; do
    if [ -r $i ]; then
      . $i
    fi
  done
  unset i
fi

Notice it loops through /etc/profile.d/*.sh — this directory holds additional system-wide configuration snippets, often installed by packages.

/etc/profile.d/

Individual packages can add their own system-wide login configuration by dropping a .sh file here:

$ ls /etc/profile.d/
apps-bin-path.sh  bash_completion.sh  cedilla-portuguese.sh  vte-2.91.sh

This modular approach lets packages contribute to the login environment without modifying /etc/profile directly.

/etc/bash.bashrc

Read by all interactive non-login shells, for all users, before their personal .bashrc:

$ cat /etc/bash.bashrc
# System-wide .bashrc file for interactive bash(1) shells.
if [ -z "$PS1" ]; then
   return
fi
...

System-Wide vs. Personal: When to Use Each

Use system-wide files (/etc/profile, /etc/bash.bashrc, /etc/profile.d/) when configuring behavior for all users on a shared system — a company standard prompt, a PATH addition needed by every user, a company-wide alias.

Use personal files (~/.bashrc, ~/.profile) for your own individual customization — your personal aliases, your preferred prompt, your specific PATH additions.

Applying Changes: source and Restart

After editing any shell startup file, the changes do not automatically apply to your currently running shell — you need to either open a new terminal or explicitly reload the file:

$ source ~/.bashrc
# or the shorter equivalent:
$ . ~/.bashrc

source (or the . shorthand) reads and executes the file’s commands in your current shell, applying any changes immediately without needing a new terminal window.

For login file changes, you generally need to fully log out and back in (or start a new login shell) since the login sequence only runs once per session:

$ bash --login    # Start a new login shell to test .profile/.bash_profile changes

Diagnosing Shell Startup Issues

Checking Which Files Are Being Read

Add temporary debug lines to see which files execute:

# Add to the top of each file you want to trace
echo "Reading /etc/profile" >&2
echo "Reading ~/.bashrc" >&2
echo "Reading ~/.profile" >&2

Open a new terminal (or SSH in) and observe which messages appear and in what order.

Checking If a Shell Is a Login Shell

$ echo $0
-bash        # Leading dash indicates login shell
bash         # No leading dash: non-login shell

Or more explicitly:

$ shopt login_shell
login_shell     on      # This is a login shell
login_shell     off     # This is not

Checking If a Shell Is Interactive

$ echo $-
himBHs      # Contains 'i' = interactive

If the output contains the letter i, the shell is interactive.

Common Problem: “It Works When I Log In via SSH But Not in a New Terminal Tab”

This classic symptom means your configuration is in a login-only file (~/.bash_profile or ~/.profile) but not sourced by ~/.bashrc. SSH sessions are login shells (reading the login files); new terminal tabs on your desktop are typically non-login shells (reading only ~/.bashrc).

Fix: Either move the configuration to ~/.bashrc, or ensure your ~/.bash_profile/~/.profile sources ~/.bashrc (as the Ubuntu default does).

Common Problem: “It Works When I Open a New Terminal But Not Over SSH”

The opposite symptom means your configuration is in ~/.bashrc but your login shell setup does not source it. Check your ~/.profile or ~/.bash_profile for the . ~/.bashrc sourcing line; add it if missing:

# Add to ~/.bash_profile if missing
if [ -f ~/.bashrc ]; then
    . ~/.bashrc
fi

Other Shell Configuration Files Worth Knowing

~/.bash_logout

Runs when a login shell exits (logging out). Useful for cleanup tasks:

# ~/.bash_logout
clear    # Clear the terminal screen on logout, for privacy on shared systems

~/.inputrc

Configures Readline (the library handling command-line editing, history search, and tab completion) independent of which shell you use:

# ~/.inputrc
set completion-ignore-case on    # Case-insensitive tab completion
set show-all-if-ambiguous on     # Show all matches immediately on ambiguous tab
"\e[A": history-search-backward   # Up arrow searches history matching typed prefix
"\e[B": history-search-forward

/etc/environment

A special file (not a shell script — just KEY=value pairs) that sets system-wide environment variables for all processes, not just shells:

$ cat /etc/environment
PATH="/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"

Unlike /etc/profile, this file is read by the PAM (Pluggable Authentication Modules) system during login, before any shell even starts — making it the earliest point for setting truly universal environment variables.

A Complete Recommended Setup

For a typical desktop Linux user, here is a sensible way to organize your configuration:

~/.bashrc (interactive shell settings — the file you will edit most):

# History
HISTSIZE=10000
HISTFILESIZE=20000
HISTCONTROL=ignoredups:erasedups
shopt -s histappend

# Aliases
alias ll='ls -la'
alias grep='grep --color=auto'
alias update='sudo apt update && sudo apt upgrade'

# Prompt
export PS1='\[\033[01;32m\]\u@\h\[\033[00m\]:\[\033[01;34m\]\w\[\033[00m\]\$ '

# Functions
mkcd() {
    mkdir -p "$1" && cd "$1"
}

# Bash completion
if [ -f /etc/bash_completion ]; then
    . /etc/bash_completion
fi

# Load machine-specific overrides if present
if [ -f ~/.bashrc.local ]; then
    . ~/.bashrc.local
fi

~/.profile (login/environment settings — usually left at Ubuntu’s default, with minor additions):

# Ubuntu's default content (sources .bashrc) plus:

export EDITOR=nano
export PATH="$HOME/.local/bin:$HOME/.cargo/bin:$PATH"

This structure keeps interactive customization in .bashrc (loaded everywhere) while environment/PATH setup lives in .profile (loaded once at login and inherited by everything spawned from that session, including graphical applications launched from a login shell).

Quick Reference: Shell Startup Files

File Scope When It Runs
/etc/environment System-wide Before login, via PAM (not a shell script)
/etc/profile System-wide Login shells, before personal profile
/etc/profile.d/*.sh System-wide Sourced by /etc/profile
~/.bash_profile Personal Login shells (if exists, others in this row skipped)
~/.bash_login Personal Login shells (only if .bash_profile absent)
~/.profile Personal Login shells (only if the above two absent)
/etc/bash.bashrc System-wide Interactive non-login shells
~/.bashrc Personal Interactive non-login shells (and login shells, if sourced)
~/.bash_logout Personal Login shell exit
~/.inputrc Personal Readline configuration (any shell)

Conclusion: A Design That Rewards Understanding

The multiplicity of Bash startup files reflects genuine engineering logic from an earlier era of computing, even though it causes real confusion today. Once you understand the core distinction — login vs. non-login, interactive vs. non-interactive — the behavior of every file falls into place: .bashrc for what every interactive shell needs, .profile/.bash_profile for what should happen once per session, and the /etc/ equivalents for system-wide versions of both.

For the vast majority of desktop Linux use, the practical takeaway is simple: put your aliases, functions, and prompt customization in ~/.bashrc. Because Ubuntu and most desktop distributions already configure ~/.profile to source ~/.bashrc, this single file covers nearly every situation you will encounter. Understanding the deeper distinction becomes valuable when you manage servers, write portable scripts, or need to diagnose why a setting behaves differently across SSH sessions and desktop terminal windows — situations where knowing exactly which file runs when transforms confusion into clarity.

Hot this week

Type Traits in C++: Compile-Time Type Information

Master C++ type traits — learn how to query and transform types at compile time using , write your own type traits, and use them with if constexpr and SFINAE.

What Is the Difference Between sh, bash, and zsh Shells?

Learn the difference between sh, bash, and zsh shells in Linux — their history, compatibility, features, and how to choose the right shell for scripting and daily use.

How to View File Contents in Linux Without Opening an Editor

Learn how to view file contents in Linux using cat, less, more, head, tail, and other commands — without opening a text editor. Perfect for quick checks and log monitoring.

Understanding Pipes in Linux: Chaining Commands Together

Learn how Linux pipes work, how to chain commands together with the | operator, practical pipe examples, and the difference between pipes and redirection.

How to Redirect Output in Linux Commands

Learn how to redirect stdout, stderr, and stdin in Linux using >, >>, <, 2>, and 2>&1. Master output redirection with practical examples for scripts and everyday commands.

Topics

Type Traits in C++: Compile-Time Type Information

Master C++ type traits — learn how to query and transform types at compile time using , write your own type traits, and use them with if constexpr and SFINAE.

What Is the Difference Between sh, bash, and zsh Shells?

Learn the difference between sh, bash, and zsh shells in Linux — their history, compatibility, features, and how to choose the right shell for scripting and daily use.

How to View File Contents in Linux Without Opening an Editor

Learn how to view file contents in Linux using cat, less, more, head, tail, and other commands — without opening a text editor. Perfect for quick checks and log monitoring.

Understanding Pipes in Linux: Chaining Commands Together

Learn how Linux pipes work, how to chain commands together with the | operator, practical pipe examples, and the difference between pipes and redirection.

How to Redirect Output in Linux Commands

Learn how to redirect stdout, stderr, and stdin in Linux using >, >>, <, 2>, and 2>&1. Master output redirection with practical examples for scripts and everyday commands.

How to Create and Edit Text Files from the Command Line

Learn every way to create and edit text files from the Linux command line — touch, cat, echo, redirection, heredocs, and terminal editors like nano and vim.

Understanding Linux Software Sources and PPAs

Learn what PPAs are, how Ubuntu software sources work, how to safely add and remove PPAs, and the risks and benefits of using third-party software repositories.

How to Search for Installed Packages in Linux

Learn how to check if a package is installed in Linux and list all installed packages using dpkg -l, rpm -qa, apt list --installed, and dnf list installed.

Related Articles

Popular Categories