LFCS 45 ๐ง Shell Configuration Files โ .bashrc, .profile
When a Bash shell starts, it does not simply present a prompt and wait. It reads one or more configuration files first, and the contents of those files determine which variables are set, which aliases exist, and how the environment is shaped. The confusion that surrounds shell configuration files comes from the fact that Bash reads different files depending on how the shell was started. A login shell over SSH does not read the same files as a terminal window opened inside a graphical session, and that terminal window does not read the same files as a non-interactive script .
The two files that cause the most confusion are ~/.bashrc and ~/.profile. The common description is that .bashrc is for interactive shells and .profile is for login shells, but that simplification hides the actual mechanics. Bash reads ~/.bash_profile, ~/.bash_login, and ~/.profile in that order when it is a login shell, and it reads only the first one that exists . It reads ~/.bashrc when it is an interactive non-login shell . A typical desktop terminal is an interactive non-login shell, so it reads .bashrc but not .profile. An SSH session is an interactive login shell, so it reads .profile (or .bash_profile) but not .bashrc automatically .
For the LFCS exam, “Use shell/environment variables” is the objective, and understanding which file to edit is the practical consequence of that objective . The rule of thumb that works on most systems is: put environment variables and anything that must be inherited by child processes in ~/.profile (or ~/.bash_profile), and put aliases, functions, and prompt customizations in ~/.bashrc. Many distributions also have ~/.profile source ~/.bashrc so that login shells get both sets of settings .
Key point: Bash reads ~/.bash_profile, ~/.bash_login, then ~/.profile for login shells, and ~/.bashrc for interactive non-login shells; on most systems, ~/.bash_profile or ~/.profile should source ~/.bashrc so that login shells inherit both environment variables and interactive settings.
Why the distinction exists
The login vs non-login problem. A login shell is the first shell started after authenticationโan SSH connection, a console login, or su -. A non-login shell is any shell started after that: a terminal emulator window, a subshell, or bash typed at a prompt . Bash needs to know this because some settings should run once per login (environment setup, resource limits) and some should run every time an interactive shell starts (aliases, prompt) .
The ~/.profile fallback chain. Bash does not read all three login files. It reads ~/.bash_profile if it exists; if not, it tries ~/.bash_login; if neither exists, it falls back to ~/.profile . This chain exists for compatibility: .profile is the original Bourne shell file, .bash_profile is Bash-specific, and .bash_login is a C shell convention. You should use only one of them. If ~/.bash_profile exists, ~/.profile is ignored entirely .
The ~/.bashrc gap. A login shell does not read ~/.bashrc automatically. This means an SSH session that reads only ~/.profile will not get the aliases and functions defined in ~/.bashrc unless ~/.profile explicitly sources it. The standard fix is a line in ~/.bash_profile or ~/.profile that checks for ~/.bashrc and sources it if present . Many distributions ship a default ~/.profile that does this, but not all do, and the omission is a common source of “my alias works in the terminal but not over SSH” confusion.
The system-wide layer. Before any user file, Bash reads /etc/profile for login shells . Files in /etc/profile.d/ with a .sh extension are sourced by /etc/profile and apply to all users. This is where system administrators put environment variables that every user should inherit, such as JAVA_HOME or a custom PATH addition . For interactive non-login shells, Bash reads /etc/bash.bashrc on Debian-based systems, though this varies by distribution.
The non-interactive silence. A shell running a script is non-interactive. Bash does not read ~/.bashrc or ~/.profile for non-interactive shells unless the script explicitly sources them or BASH_ENV is set . This is why environment variables set in a terminal do not automatically appear in cron jobs or systemd services. The configuration files are for interactive use; scripts get the environment they are given.
a. The login shell files: ~/.bash_profile, ~/.bash_login, ~/.profile
When Bash starts as a login shell, it reads /etc/profile first, then searches for the first readable file among ~/.bash_profile, ~/.bash_login, and ~/.profile . It does not read all threeโit reads exactly one. The choice of which file to create is a matter of convention and compatibility.
~/.profile is the most portable choice. It is read by Bash, sh, ksh, and other Bourne-compatible shells, so it works even if the user changes shells . It is the right file for environment variables that should be inherited by all processes: PATH, EDITOR, LANG, and similar settings .
~/.bash_profile is Bash-specific. It is the conventional choice on Red Hat-based systems, while Debian-based systems tend to favor ~/.profile . If you use ~/.bash_profile, you should include a snippet to source ~/.bashrc so that aliases and functions are available in login shells:
if [ -f ~/.bashrc ]; then
. ~/.bashrc
fi
This snippet is the standard bridge between the two files. Without it, an SSH session will have the environment variables from .bash_profile but not the aliases from .bashrc.
~/.bash_login is rarely used. It exists for compatibility with the C shell’s .login convention and is generally not created on modern systems. If it exists, it takes precedence over .profile.
b. The interactive non-login file: ~/.bashrc
~/.bashrc is read every time an interactive non-login shell starts. This is the shell you get when you open a terminal emulator in a graphical desktop, or when you type bash at an existing prompt . It is also read by some SSH configurations for non-interactive remote commands, depending on how Bash detects the connection .
The content of ~/.bashrc should be settings that are specific to interactive use and do not need to be inherited by child processes. Aliases are the classic example:
alias ll='ls -alF'
alias grep='grep --color=auto'
Functions, shell options (set -o), and prompt customization (PS1) also belong here . These settings do not make sense for a script, and they should not be exported to child processes.
A common mistake is to put export PATH=... in ~/.bashrc. This works for terminal windows, but it does not apply to SSH login sessions unless .bashrc is sourced from .profile. The PATH modification belongs in .profile or .bash_profile if it should apply to all sessions .
On Debian and Ubuntu, the default ~/.bashrc contains a guard at the top that returns early if the shell is not interactive. This prevents the file from running its aliases and prompt settings in non-interactive contexts. The guard looks like this:
case $- in
*i*) ;;
*) return;;
esac
This is why sourcing .bashrc from .profile is safe on Debian-based systemsโthe guard prevents non-interactive shells from executing the interactive-only parts.
c. System-wide configuration and the /etc/profile.d/ directory
User configuration files apply to one user. System-wide configuration applies to everyone. The primary file for system-wide login settings is /etc/profile, which is read before any user file . On Debian-based systems, /etc/bash.bashrc serves the same role for interactive non-login shells.
The /etc/profile.d/ directory is where system administrators place modular configuration scripts. /etc/profile contains a loop that sources every .sh file in that directory . This is the standard location for software that needs to set environment variables for all usersโJava, Go, Node.js, and similar tools. A file like /etc/profile.d/java.sh might contain:
export JAVA_HOME=/usr/lib/jvm/default-java
export PATH="$JAVA_HOME/bin:$PATH"
The advantage of /etc/profile.d/ over editing /etc/profile directly is modularity and upgrade safety. Package managers can add and remove files in this directory without conflicting with each other or with administrator edits to /etc/profile .
For the LFCS exam, knowing that /etc/profile.d/*.sh is sourced by /etc/profile is the key fact. The exam objective “Manage system-wide environment profiles” points directly at this mechanism .
Complete Example Session
The following session demonstrates the difference between login and non-login shells and the correct placement of configuration.
# ============================================
# PART 1: CHECK CURRENT SHELL TYPE
# ============================================
echo $0
# Output: -bash (the leading dash indicates a login shell)
# ============================================
# PART 2: CREATE A TEST VARIABLE IN .profile
# ============================================
echo 'export TEST_PROFILE="from profile"' >> ~/.profile
# ============================================
# PART 3: CREATE A TEST ALIAS IN .bashrc
# ============================================
echo 'alias testalias="echo from bashrc"' >> ~/.bashrc
# ============================================
# PART 4: OPEN A NEW TERMINAL (non-login shell)
# ============================================
# In a terminal emulator:
echo $TEST_PROFILE
# Output: (empty) โ .profile not read
testalias
# Output: from bashrc โ .bashrc read
# ============================================
# PART 5: START A LOGIN SHELL
# ============================================
bash --login
echo $TEST_PROFILE
# Output: from profile โ .profile read
testalias
# Output: (command not found) โ .bashrc not read
# (unless .profile sources .bashrc)
# ============================================
# PART 6: ADD THE BRIDGE TO .profile
# ============================================
# Add to ~/.profile:
# if [ -f ~/.bashrc ]; then . ~/.bashrc; fi
# ============================================
# PART 7: VERIFY AFTER BRIDGE
# ============================================
bash --login
testalias
# Output: from bashrc โ now .bashrc is sourced
# ============================================
# PART 8: SYSTEM-WIDE VARIABLE
# ============================================
sudo tee /etc/profile.d/test.sh << 'EOF'
export SYSTEM_WIDE="available to all users"
EOF
# ============================================
# PART 9: VERIFY SYSTEM-WIDE (new login shell)
# ============================================
bash --login
echo $SYSTEM_WIDE
# Output: available to all users
# ============================================
# PART 10: CLEANUP
# ============================================
# Remove test lines from ~/.profile and ~/.bashrc
# sudo rm /etc/profile.d/test.sh
The ten parts cover checking the shell type, adding a variable to .profile, adding an alias to .bashrc, testing in a non-login shell, testing in a login shell, adding the bridge, verifying the bridge, creating a system-wide variable, verifying system-wide access, and cleanup. What the session demonstrates is that the file that runs depends entirely on how the shell was started, and that the bridge from login files to .bashrc is what makes settings consistent across session types.
Quick Reference
Which File Runs When
| Shell Type | Files Read (in order) |
|---|---|
| Login (SSH, console) | /etc/profile, then first of ~/.bash_profile, ~/.bash_login, ~/.profile |
| Interactive non-login (terminal) | /etc/bash.bashrc (Debian), ~/.bashrc |
| Non-interactive (script) | None (unless BASH_ENV set) |
Login File Precedence
| File | Read If |
|---|---|
~/.bash_profile | Exists and readable (first priority) |
~/.bash_login | .bash_profile does not exist |
~/.profile | Neither of the above exists |
What Goes Where
| Setting | File |
|---|---|
PATH, EDITOR, LANG | ~/.profile or ~/.bash_profile |
| Aliases, functions | ~/.bashrc |
Prompt (PS1) | ~/.bashrc |
| System-wide variables | /etc/profile.d/*.sh |
Common Commands
| Command | Effect |
|---|---|
bash --login | Start a login shell |
bash | Start a non-login interactive shell |
source ~/.bashrc | Apply .bashrc changes immediately |
echo $0 | Show current shell (leading - means login) |
Best Practices
โ Do This:
# ~/.profile
export PATH="$HOME/bin:$PATH" # โ
inherited variable
if [ -f ~/.bashrc ]; then . ~/.bashrc; fi # โ
bridge to .bashrc
# ~/.bashrc
alias ll='ls -alF' # โ
interactive only
sudo tee /etc/profile.d/app.sh <<< 'export APP_HOME=/opt/app' # โ
system-wide
โ Don’t Do This:
# ~/.bashrc
export PATH="$HOME/bin:$PATH" # โ not read by SSH login
# ~/.profile
alias ll='ls -alF' # โ not interactive
# create both ~/.bash_profile and ~/.profile # โ .profile ignored
Common Pitfalls
| Pitfall | Why It Happens | Fix |
|---|---|---|
| Alias works in terminal, not SSH | .bashrc not sourced by login shell | Add bridge to .profile |
| Variable missing in terminal | Set in .profile, not .bashrc | Move to .bashrc or source it |
.profile ignored | .bash_profile exists | Use only one login file |
| System-wide variable not applied | File not in /etc/profile.d/ | Place .sh file there |
| Changes not applied | Shell not reloaded | source file or new shell |
Real-World Examples
1. Add PATH in .profile
export PATH="$HOME/bin:$PATH"
2. Bridge to .bashrc
if [ -f ~/.bashrc ]; then . ~/.bashrc; fi
3. Alias in .bashrc
alias ll='ls -alF'
4. System-wide Java home
sudo tee /etc/profile.d/java.sh <<< 'export JAVA_HOME=/usr/lib/jvm/default'
5. Check shell type
echo $0 # -bash = login, bash = non-login
6. Reload .bashrc
source ~/.bashrc
7. Force login shell
bash --login
8. Force non-login shell
bash
9. Skip config files
bash --noprofile --norc
10. View /etc/profile.d/
ls /etc/profile.d/
Visual
Login vs Non-Login Flow
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ SSH LOGIN (login shell) โ
โ โ โ
โ โผ โ
โ /etc/profile โ
โ โ โ
โ โผ โ
โ ~/.bash_profile OR ~/.bash_login OR ~/.profileโ
โ โ โ
โ โผ โ
โ (does NOT read ~/.bashrc unless sourced) โ
โ โ
โ TERMINAL (interactive non-login) โ
โ โ โ
โ โผ โ
โ /etc/bash.bashrc (Debian) โ
โ โ โ
โ โผ โ
โ ~/.bashrc โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
The Bridge
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ ~/.profile โ
โ export PATH=... โ environment โ
โ if [ -f ~/.bashrc ]; then . ~/.bashrc; fi โ
โ โ โ
โ โผ โ
โ ~/.bashrc โ
โ alias ll='ls -alF' โ interactive โ
โ PS1=... โ
โ โ
โ Result: SSH gets both environment and aliasesโ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
File Precedence
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ Login shell file search: โ
โ โ
โ 1. ~/.bash_profile โ if exists, stop โ
โ 2. ~/.bash_login โ if exists, stop โ
โ 3. ~/.profile โ fallback โ
โ โ
โ Only ONE of these is read. โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
System-Wide Layer
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ /etc/profile โ
โ sources /etc/profile.d/*.sh โ
โ โ โ
โ โผ โ
โ /etc/profile.d/java.sh โ
โ /etc/profile.d/node.sh โ
โ /etc/profile.d/custom.sh โ
โ โ โ
โ โผ โ
โ All users get these variables โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
Summary
| Item | Value |
|---|---|
| Login shell files | ~/.bash_profile, ~/.bash_login, ~/.profile (first found) |
| Interactive non-login file | ~/.bashrc |
| System-wide login | /etc/profile |
| System-wide drop-in | /etc/profile.d/*.sh |
| Bridge snippet | if [ -f ~/.bashrc ]; then . ~/.bashrc; fi |
~/.profile purpose | Environment variables, inherited settings |
~/.bashrc purpose | Aliases, functions, prompt |
| Shell type check | echo $0 (leading - = login) |
Key takeaways:
- Bash reads different files depending on shell type. Login shells read
~/.bash_profile/~/.bash_login/~/.profile; interactive non-login shells read~/.bashrc. - Only the first login file found is read. If
~/.bash_profileexists,~/.profileis ignored entirely . .profileis for environment variables;.bashrcis for interactive settings. PATH and exported variables go in.profile; aliases and functions go in.bashrc.- The bridge makes login shells read
.bashrc. Addif [ -f ~/.bashrc ]; then . ~/.bashrc; fito.profileor.bash_profile. /etc/profile.d/is for system-wide variables. Files there are sourced by/etc/profilefor all users .- Non-interactive shells read nothing. Scripts and cron jobs do not read these files; they inherit only exported environment variables.
Remember: The shell configuration file that runs depends on how the shell was started. A login shell reads /etc/profile and then one of ~/.bash_profile, ~/.bash_login, or ~/.profile. An interactive non-login shell reads ~/.bashrc. The common practice is to put environment variables in .profile and interactive settings in .bashrc, then source .bashrc from .profile so that login shells get both. System-wide settings go in /etc/profile.d/. For the LFCS exam, know the order, know the precedence, and know the bridgeโthat is the practical difference between a configuration that works everywhere and one that works only in the terminal where you tested it.
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!