| | |

LFCA 61 ๐Ÿง Shell Configuration Files โ€” .bashrc, .profile

Every time you open a terminal, log in via SSH, or start a new shell session, Bash reads one or more configuration files to set up your environment. These files control your PATH, define aliases, customize your prompt, export variables that programs rely on, and decide whether your favorite shortcuts exist at all. They are the first thing that runs when you sit down at a machine, and the last thing most people think about when something “mysteriously stops working.”

The LFCA exam places shell configuration under Linux Fundamentals, which carries 16โ€“20% of the total weight. The competency list includes “command line” and “basic programming,” and both depend on understanding where your shell settings live. A user who does not know why their .bashrc aliases disappear when they SSH into a server will waste hours blaming the wrong thing. A user who understands the login/non-login split will fix it in thirty seconds.

Key point: Bash reads different startup files depending on whether the shell is a login shell or an interactive non-login shell. A login shell is created when you authenticate โ€” via SSH, a TTY console, or su -. An interactive non-login shell is created when you open a new terminal window inside a graphical session or run bash from an existing shell. The file that runs for a login shell is not the same file that runs for every new terminal.

This distinction is not arbitrary. It exists because login happens once per session, while new interactive shells may be spawned dozens of times. Settings that need to run once โ€” environment variables, PATH manipulation, locale โ€” belong in the login file. Settings that matter only to interactive shells โ€” aliases, prompt customization, shell functions โ€” belong in the file that runs every time you open a terminal.


Why shell configuration files exist

Before these files existed, every user had to set their environment manually after logging in. They typed export PATH=$PATH:/opt/bin, defined their aliases by hand, and customized their prompt from scratch. Every session. Every login. Every new terminal. The moment you opened a second window, you started over.

The persistence problem. A shell is a process. When the process exits, everything it held in memory is gone โ€” variables, aliases, functions, the prompt. Without configuration files, every session begins with a blank slate. The only way to preserve settings across sessions is to store them somewhere on disk and read them when the shell starts. This is the same principle that makes any configuration file useful: separate the settings from the process so they survive restarts.

The context problem. Not every setting should apply to every shell. A shell script should not inherit your aliases โ€” an ls alias in an interactive shell could silently change how a script behaves. An interactive shell should not be forced to run expensive environment setup every time it starts. The login/non-login split is a solution to this: put environment variables in the login file (run once), and interactive settings in the interactive file (run every time). Each file serves a purpose that the other cannot.

The user-vs-system problem. Some settings apply to every user โ€” the default PATH, system-wide aliases, security policies. Others are personal โ€” your editor preference, your prompt format, your aliases. The filesystem splits these into /etc/profile for system-wide settings and ~/.profile, ~/.bashrc, ~/.bash_profile for user-specific settings. The system files run first; the user files run after, so a user can override system defaults without changing the system.

The portability problem. Different shells read different files. Bash reads .bashrc. Zsh reads .zshrc. The POSIX standard defines .profile for login shells regardless of which shell you use. A user who switches from Bash to Zsh discovers their aliases are gone โ€” because .bashrc is not read by Zsh. The .profile file is the portable layer; .bashrc and .zshrc are shell-specific.

The trade-off. This system is not simple. There are five or six files, depending on which Linux distribution you use, and Bash reads different ones depending on how the shell was started. The complexity is real. But the alternative โ€” requiring users to configure their environment from scratch every login โ€” is worse. The configuration file system is a thirty-year-old compromise that solves more problems than it creates.


a. The Two Shell Types and Their Files

The distinction between login and non-login shells is the foundation for everything else in this chapter. If you misunderstand it, every other rule will seem arbitrary. If you understand it, the rest of the system is obvious.

A login shell is created when you first authenticate to a system. This happens with SSH connections, TTY logins, and su - commands. The login shell runs the login sequence, sets up your environment, and then presents a prompt. Login shells are the outermost layer of your session; everything you do happens inside them.

When Bash starts as a login shell, it reads configuration in a specific order. First, /etc/profile โ€” this is the system-wide login file, maintained by your distribution, and it sets up baseline environment variables for all users. The file typically sources scripts in /etc/profile.d/ โ€” modular configuration files that each package installs to add its own settings. This is where the LANG environment variable often gets set, and where PATH gets its standard entries.

After /etc/profile, Bash searches for user-specific login files. It looks for ~/.bash_profile first. If that does not exist, it looks for ~/.bash_login. If that does not exist, it looks for ~/.profile. It reads the first one it finds and stops โ€” it does not continue to the next file even if the first one is empty. This “first one wins” rule is the source of the most common configuration trap.

A non-login interactive shell is created when you open a new terminal window inside a graphical session, or when you type bash inside an existing shell. Bash starts, sees that it was not invoked as a login shell, and reads a different set of files. It reads ~/.bashrc if it exists. It does not read /etc/profile, does not read .bash_profile, does not read .profile โ€” unless one of those files explicitly sources it.

This split exists for a reason. The login sequence runs once per session. Everything in /etc/profile and .bash_profile executes a single time โ€” when you first log in. If those files were read every time you opened a new terminal, you would pay the cost of environment setup repeatedly. The .bashrc file, by contrast, runs on every terminal open, which is fine because aliases and prompt settings are cheap.

The practical implication is that .bashrc is not read automatically on login. If you SSH into a machine and your aliases are defined only in .bashrc, they will be missing. You will see a working shell with no shortcuts. The fix is a single line in ~/.bash_profile:

if [ -f "$HOME/.bashrc" ]; then
    . "$HOME/.bashrc"
fi

The . "$HOME/.bashrc" syntax โ€” equivalent to source "$HOME/.bashrc" โ€” tells the login shell to read .bashrc in addition to the login files. This is the standard pattern on most Linux distributions. Many distributions generate a .bash_profile with exactly this line by default, precisely so that new users do not fall into the trap.


b. What Goes in Each File

The separation is not arbitrary. Each file has a purpose rooted in when it runs, and putting the wrong thing in the wrong file produces behavior that looks like a bug but is actually the system working as designed.

~/.profile is the portable login file. It works with Bash, ksh, sh, and other POSIX-compatible shells. It is the right place for environment variables โ€” PATH, LANG, EDITOR, LD_LIBRARY_PATH, JAVA_HOME. These need to be inherited by all programs in the session, not just interactive shells, so they belong in the file that runs once and exports them into the environment.

The distinction is subtle but important. An environment variable must be exported โ€” export PATH=... โ€” to be inherited by child processes. A shell variable โ€” myvar="hello" โ€” is local to the shell. Both belong in login files, but only the exported ones need to be there if other programs will use them.

~/.bash_profile is the Bash-specific login file. It overrides ~/.profile if both exist. Its purpose is to hold Bash-specific login initialization, and to source .bashrc so that interactive settings are available in login sessions. A minimal .bash_profile might contain:

if [ -f "$HOME/.profile" ]; then
    . "$HOME/.profile"
fi
if [ -f "$HOME/.bashrc" ]; then
    . "$HOME/.bashrc"
fi

This pattern sources both files, ensuring that both environment variables and interactive settings are loaded in a login shell. Some users prefer to source only .bashrc, relying on .profile to be the single source of environment variables. Either approach works as long as you are consistent.

~/.bashrc runs for every interactive non-login shell. It is the right place for aliases (alias ll='ls -la'), shell functions (reusable snippets of logic), prompt customization (PS1), shopt options (shell behavior flags), and command-line completions. These settings matter only to interactive shells, so they should not be inherited by scripts.

The reason aliases do not belong in .profile is that scripts do not read .profile โ€” they are non-interactive and read $BASH_ENV if set, otherwise nothing. If you define alias ls='ls --color=auto' in .profile and run a script, the script will not have the alias. But if the script somehow did read the alias, it might behave unexpectedly โ€” an alias that changes the output format of ls could break a script that parses ls output. Keeping aliases out of non-interactive contexts prevents this class of bug.

A common recommendation is to put base configuration in .bashrc and source it from .bash_profile, so that you have one file to edit for most customizations. Some users go further and put everything in .bashrc, leaving .bash_profile as nothing but the sourcing line. This is clean and avoids duplication, at the cost of having a nearly-empty .bash_profile that looks confusing until you understand why it exists.


c. The Precedence Trap and System-Wide Files

The order in which Bash searches for login files creates a subtle trap. If both ~/.bash_profile and ~/.profile exist, .bash_profile wins and .profile is never read. This is the most common cause of “my settings stopped working” after a user creates a .bash_profile.

The scenario is almost always the same. A user logs in, notices their aliases are missing, reads a tutorial that says “create ~/.bash_profile to fix this,” creates the file with an alias definition, and everything works. Later, they add an environment variable to .profile โ€” the file the tutorial also mentioned โ€” and it does not take effect. The reason is that .bash_profile now exists, and Bash reads it instead of .profile. The .profile settings are silently ignored.

The fix is to either remove ~/.bash_profile (letting .profile be read) or to add a sourcing line at the top of .bash_profile:

if [ -f "$HOME/.profile" ]; then
    . "$HOME/.profile"
fi

This tells the login shell to read .profile as part of its startup, so both files’ settings are loaded. The same pattern applies to .bash_login, which has the same precedence as .bash_profile โ€” but .bash_login is rarely used, and creating one when .bash_profile already exists causes it to be ignored entirely.

System-wide files apply to all users. /etc/profile is read by all login shells, regardless of user. It typically sources scripts in /etc/profile.d/ for modular configuration โ€” each .sh file in that directory can be installed by a package to add its own login-time settings. This is where the standard PATH, LANG, and LESSOPEN settings often originate. Modifying /etc/profile affects every user on the system; modifying /etc/profile.d/*.sh is the preferred approach because it keeps changes isolated.

/etc/bash.bashrc (on Debian and Ubuntu systems) or /etc/bashrc (on Red Hat family systems) is read by interactive non-login shells for all users. It is the system-wide counterpart to .bashrc. On most distributions, this file does very little; the user’s ~/.bashrc is what does the real work. But /etc/bash.bashrc is where distributions sometimes set global prompt defaults or add system-wide aliases.

/etc/environment is a third system-wide file, used by the PAM pam_env module rather than by Bash directly. It contains simple KEY=value lines โ€” no export, no shell syntax โ€” and sets environment variables for all users, regardless of which shell they use. This is the correct place for variables that need to be available to graphical sessions, systemd services, and login shells alike. Variables set in /etc/environment are available in .profile and .bashrc but not the other way around.


Complete Example Session

This session builds a working configuration from scratch, demonstrates the login/non-login split, and verifies which files are being read.

# ============================================
# PART 1: THE TYPICAL .profile CONTENT
# ============================================

# ~/.profile
# Environment variables for all shells
export PATH="$HOME/bin:$PATH"
export EDITOR="vim"
export LANG="en_US.UTF-8"
export LESS="-R"

# Source .bashrc so aliases are available in login shells too
if [ -f "$HOME/.bashrc" ]; then
    . "$HOME/.bashrc"
fi

# ============================================
# PART 2: THE TYPICAL .bashrc CONTENT
# ============================================

# ~/.bashrc
# If not running interactively, do nothing
case $- in
    *i*) ;;
      *) return;;
esac

# Aliases
alias ll='ls -la'
alias la='ls -A'
alias ..='cd ..'
alias ...='cd ../..'
alias grep='grep --color=auto'

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

# History
export HISTSIZE=10000
export HISTFILESIZE=20000
export HISTCONTROL=ignoreboth

# ============================================
# PART 3: VERIFYING WHICH FILES ARE READ
# ============================================

# Check if you are in a login shell
shopt -q login_shell && echo 'Login shell' || echo 'Not login shell'

# Check if interactive
if [ -z "$PS1" ]; then echo 'Non-interactive'; else echo 'Interactive'; fi

# ============================================
# PART 4: THE SOURCING LINE IN .bash_profile
# ============================================

# ~/.bash_profile
# Source .profile for environment variables
if [ -f "$HOME/.profile" ]; then
    . "$HOME/.profile"
fi

# Source .bashrc for interactive settings
if [ -f "$HOME/.bashrc" ]; then
    . "$HOME/.bashrc"
fi

# ============================================
# PART 5: RELOADING WITHOUT LOGGING OUT
# ============================================

source ~/.bashrc
# or the portable form:
. ~/.bashrc

# Verify the change
alias ll

# ============================================
# PART 6: THE PRECEDENCE TRAP
# ============================================

# If both .bash_profile and .profile exist:
ls -la ~/.bash_profile ~/.profile

# .bash_profile is read; .profile is ignored.
# Fix: remove .bash_profile or source .profile from it.

# Demonstrate the trap:
echo 'export TRAP_TEST="from-profile"' >> ~/.profile
# Log out and back in (or start a login shell)
bash --login -c 'echo $TRAP_TEST'  # prints nothing if .bash_profile wins

# ============================================
# PART 7: DEBUGGING WITH -x
# ============================================

# Start a login shell with tracing to see which files run
bash --login -x -c 'exit' 2>&1 | grep -E '(profile|bashrc)'

# This shows every file Bash reads during login startup.

# ============================================
# PART 8: THE SYSTEM-WIDE FILES
# ============================================

# /etc/profile โ€” read by all login shells, all users
# /etc/profile.d/*.sh โ€” modular system-wide settings
# /etc/bash.bashrc โ€” interactive non-login shells (Debian/Ubuntu)
# /etc/bashrc โ€” interactive non-login shells (RHEL family)
# /etc/environment โ€” PAM-managed, all sessions

# Check what your /etc/profile reads:
grep -E 'profile\.d|bashrc' /etc/profile

# ============================================
# PART 9: THE BASH_ENV FOR NON-INTERACTIVE
# ============================================

# For non-interactive shells (scripts), Bash reads $BASH_ENV if set
export BASH_ENV="$HOME/.bash_env"

# ~/.bash_env
# Loaded for non-interactive shells. Keep minimal.
export SCRIPT_TIMEOUT=300

# Verify in a script:
bash -c 'echo $SCRIPT_TIMEOUT'

# ============================================
# PART 10: A CLEAN SETUP CHECKLIST
# ============================================

# 1. Put environment variables in ~/.profile
# 2. Put aliases and functions in ~/.bashrc
# 3. Have ~/.bash_profile (if it exists) source ~/.bashrc
# 4. Do not create both ~/.bash_profile and ~/.bash_login
# 5. Test with: bash --login -x -c 'exit'
# 6. Use /etc/environment for system-wide variables
# 7. Use /etc/profile.d/ for system-wide login scripts
# 8. Reload with source after every change

The ten parts cover the .profile content, the .bashrc content, verification commands, the sourcing line, reloading, the precedence trap with a live demonstration, debugging with -x, system-wide files, BASH_ENV for non-interactive shells, and a setup checklist.


Quick Reference

The Configuration Files

FileWhen SourcedPurpose
~/.profileLogin shells (Bash, ksh)Environment variables, PATH
~/.bash_profileLogin shells (Bash only)Overrides .profile if present
~/.bash_loginLogin shells (fallback)Same precedence as .bash_profile
~/.bashrcInteractive non-login shellsAliases, functions, prompt
/etc/profileAll login shells, all usersSystem-wide login settings
/etc/profile.d/*.shSourced by /etc/profileModular system-wide settings
/etc/bash.bashrcInteractive non-login shellsSystem-wide interactive settings
/etc/environmentPAM for all sessionsSystem-wide env vars (no shell syntax)

The Shell Types

Shell TypeFiles Read (Bash)
Interactive login/etc/profile, then ~/.bash_profile or ~/.bash_login or ~/.profile
Interactive non-login~/.bashrc
Non-interactive (script)$BASH_ENV if set, otherwise nothing

The Precedence Rules

ComparisonWinner
~/.bash_profile vs ~/.bash_login vs ~/.profileFirst one that exists
User file vs system fileUser file runs after, can override
/etc/profile.d/ vs /etc/profileProfile.d sourced by profile

The Interactive Guard

GuardPurpose
case $- in *i*) ;; *) return;; esacExit early if not interactive
[ -z "$PS1" ] && returnAlternative interactive check

Best Practices

โœ… Do This:

# Put environment variables in .profile
export PATH="$HOME/bin:$PATH"                                # โœ…
# Put aliases and functions in .bashrc
alias ll='ls -la'                                            # โœ…
# Have .bash_profile source .bashrc
if [ -f "$HOME/.bashrc" ]; then . "$HOME/.bashrc"; fi        # โœ…
# Add interactive guard at the top of .bashrc
case $- in *i*) ;; *) return;; esac                          # โœ…
# Use /etc/environment for system-wide variables
KEY=value                                                    # โœ…
# Use /etc/profile.d/ for modular system config
# /etc/profile.d/custom.sh
export CUSTOM_VAR="value"                                    # โœ…

โŒ Don’t Do This:

# Don't put aliases in .profile
alias ll='ls -la'  # in .profile โ€” won't load in non-login shells  # โŒ
# Don't assume .bashrc loads on SSH login
# It doesn't, unless .bash_profile sources it.               # โŒ
# Don't create both .bash_profile and .bash_login
# The second is ignored.                                     # โŒ
# Don't use export in /etc/environment
export KEY=value                                             # โŒ
# Don't let .bashrc run in scripts
# Always add the interactive guard.                          # โŒ

Common Pitfalls

PitfallWhy It HappensFix
Aliases missing after SSH.bashrc not sourced on loginAdd sourcing line to .bash_profile
.profile changes ignored.bash_profile exists and winsRemove .bash_profile or source .profile
PATH not updated in scripts.profile not read for non-interactiveUse BASH_ENV or export in script
Duplicate PATH entriesSourced multiple timesGuard with case ":$PATH:" in *":$dir:"*)
.bashrc runs in scriptsNo interactive guardAdd case $- in *i*) ;; *) return;; esac
/etc/environment syntax errorUsing export keywordUse bare KEY=value lines only
Changes don’t take effectShell already runningRun source or open new shell

Real-World Examples

1. SSH Login

# ~/.profile sets PATH
export PATH="$HOME/bin:$PATH"

# ~/.bash_profile sources .bashrc for aliases
if [ -f "$HOME/.bashrc" ]; then . "$HOME/.bashrc"; fi

2. New Terminal Window

# ~/.bashrc loads aliases and prompt
alias ll='ls -la'
export PS1='\u@\h:\w\$ '

3. Shell Script

# ~/.bash_env defines variables for non-interactive use
export SCRIPT_TIMEOUT=300

4. Root User

# /root/.profile and /root/.bashrc follow the same split
# but apply only to the root user.

5. Multi-Shell User

# ~/.profile for POSIX compatibility
export PATH="$HOME/bin:$PATH"

# ~/.bashrc for Bash-specific settings
alias ll='ls -la'

6. System Package Install

# /etc/profile.d/custom-tool.sh
export PATH="/opt/custom-tool/bin:$PATH"

7. Docker Container

# Use ENV directives, not .bashrc
ENV PATH="/app/bin:$PATH"
ENV NODE_ENV=production

8. Git Bash on Windows

# Reads ~/.bash_profile and ~/.bashrc from $HOME
# $HOME is typically /c/Users/<username>

9. WSL Ubuntu

# Uses the standard Debian-family layout
# /etc/bash.bashrc is read by interactive shells

10. Shared Server

# /etc/profile.d/ used for shared configuration across users
# /etc/profile.d/company-aliases.sh
alias deploy='kubectl apply -f deployment.yaml'

Visual

The Login Shell Startup

โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚  LOGIN SHELL STARTUP                         โ”‚
โ”‚                                              โ”‚
โ”‚  ssh user@host                               โ”‚
โ”‚    โ”‚                                         โ”‚
โ”‚    โ–ผ                                         โ”‚
โ”‚  /etc/profile                                โ”‚
โ”‚    โ”œโ”€ sources /etc/profile.d/*.sh            โ”‚
โ”‚    โ””โ”€ sets system PATH, LANG                 โ”‚
โ”‚    โ”‚                                         โ”‚
โ”‚    โ–ผ                                         โ”‚
โ”‚  ~/.bash_profile (first found)               โ”‚
โ”‚    โ”œโ”€ sources ~/.profile                     โ”‚
โ”‚    โ””โ”€ sources ~/.bashrc                      โ”‚
โ”‚    โ”‚                                         โ”‚
โ”‚    โ–ผ                                         โ”‚
โ”‚  Prompt ready                                โ”‚
โ”‚                                              โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜

The Non-Login Shell Startup

โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚  NON-LOGIN INTERACTIVE SHELL                 โ”‚
โ”‚                                              โ”‚
โ”‚  Open new terminal                           โ”‚
โ”‚    โ”‚                                         โ”‚
โ”‚    โ–ผ                                         โ”‚
โ”‚  ~/.bashrc                                   โ”‚
โ”‚    โ””โ”€ aliases, functions, PS1                โ”‚
โ”‚    โ”‚                                         โ”‚
โ”‚    โ–ผ                                         โ”‚
โ”‚  Prompt ready                                โ”‚
โ”‚                                              โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜

The Precedence Trap

โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚  THE PRECEDENCE TRAP                         โ”‚
โ”‚                                              โ”‚
โ”‚  Files present:                              โ”‚
โ”‚    ~/.bash_profile  โ† read first, wins       โ”‚
โ”‚    ~/.profile       โ† IGNORED                โ”‚
โ”‚                                              โ”‚
โ”‚  Result: .profile settings never load.       โ”‚
โ”‚                                              โ”‚
โ”‚  Fix: source .profile from .bash_profile     โ”‚
โ”‚    if [ -f ~/.profile ]; then                โ”‚
โ”‚      . ~/.profile                            โ”‚
โ”‚    fi                                        โ”‚
โ”‚                                              โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜

What Goes Where

โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚  WHAT GOES WHERE                             โ”‚
โ”‚                                              โ”‚
โ”‚  ~/.profile       โ†’ PATH, EDITOR, LANG       โ”‚
โ”‚  ~/.bashrc        โ†’ aliases, functions, PS1  โ”‚
โ”‚  ~/.bash_profile  โ†’ source the two above     โ”‚
โ”‚  /etc/environment โ†’ system-wide KEY=value    โ”‚
โ”‚  /etc/profile.d/  โ†’ system-wide login scriptsโ”‚
โ”‚                                              โ”‚
โ”‚  Login shell reads .profile + .bashrc        โ”‚
โ”‚  Non-login shell reads .bashrc only          โ”‚
โ”‚  Non-interactive reads BASH_ENV (if set)     โ”‚
โ”‚                                              โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜

Summary

ConceptDefinition
Login shellCreated at login (SSH, TTY, su -), reads /etc/profile then the first user login file
Non-login shellCreated inside a session, reads ~/.bashrc
.profileEnvironment variables, runs once at login, portable
.bashrcAliases, functions, prompt, runs every interactive shell
.bash_profileBash-specific login file, overrides .profile
Precedence.bash_profile > .bash_login > .profile
Sourcing lineif [ -f ~/.bashrc ]; then . ~/.bashrc; fi in .bash_profile
BASH_ENVFile read by non-interactive shells if set
/etc/profileSystem-wide login settings
/etc/profile.d/Modular system-wide scripts
/etc/environmentPAM-managed, all sessions, no shell syntax

Key takeaways:

  • .profile is for environment variables; .bashrc is for interactive shell settings. This split exists because login shells and interactive shells serve different purposes. Environment variables need to propagate to all processes; aliases and functions only matter to interactive shells.
  • Login shells read .bash_profile before .profile. If both exist, .bash_profile wins and .profile is ignored. This is the most common cause of “my settings stopped working” after creating a .bash_profile.
  • .bashrc is not read automatically on login. A login shell only reads .bashrc if .bash_profile (or .profile) explicitly sources it. Without the sourcing line, aliases and functions are missing from SSH sessions.
  • Use source to reload without logging out. Changes to .bashrc do not take effect in the current shell until you run source ~/.bashrc or open a new terminal.
  • The case $- guard prevents .bashrc from running in non-interactive shells. Without it, a script that invokes Bash may inherit aliases and prompt customizations that break its behavior.
  • System-wide files run first, then user files. /etc/profile and /etc/profile.d/*.sh set baseline environment for all users. The user’s .profile and .bashrc run after and can override those defaults.
  • /etc/environment is the PAM-managed system-wide variable file. It uses bare KEY=value syntax โ€” no export, no shell commands โ€” and applies to all sessions, including graphical logins and systemd services.
  • Debug with bash --login -x -c 'exit'. The -x flag traces every command executed during startup, showing exactly which files are read and in what order.

Remember: .profile configures the environment; .bashrc configures the interactive shell. The login shell reads the profile first, then (if you set it up correctly) sources .bashrc for aliases and functions. Check which file wins with shopt -q login_shell, and never let an empty .bash_profile silently block your .profile settings. The configuration file system is not simple, but it is coherent โ€” every rule follows from the login/non-login distinction. Understand that distinction once, and the rest of the system is predictable.


Stop using slow, ad-bloated tool sites! ๐Ÿคฎ

๐Ÿ”Ž Search “KandZ Tools” on Google to use many professional utilities for free.

KandZ.me is the ultimate minimalist hub for:
โœ… Finance (Mortgage, Interest, Inflation)
โœ… Tech (Base64, JSON, Dev Suite, IP)
โœ… Health (BMI, BMR, TDEE)
โœ… Productivity (Timer, Workspace, QR)

โšก๏ธ Fast & Private
๐Ÿ”’ No data leaves your device
๐Ÿ’Ž 100% Free

๐Ÿ”— Use it now: https://tools.kandz.me
๐Ÿ”– Bookmark itโ€”youโ€™ll need it later!