>

I mapped Bash's startup file precedence so customizations apply predictably whether a shell is login, non-login, or interactive. Sourcing .bashrc from .bash_profile guaranteed one canonical place for aliases and environment, avoiding "works in one terminal but not another" bugs.

Objective & Context

Startup file confusion causes environment drift between SSH sessions, terminals, and cron. This lab clarifies which files run for which shell type and establishes a single source of truth, making the aliases-env customizations persistent.

Learning Objectives

  • Distinguish login, non-login, interactive, and non-interactive shells.
  • Explain which startup files each shell type reads, in order.
  • Source .bashrc from .bash_profile so settings apply everywhere.
  • Place aliases, functions, and PATH changes in the correct file.
  • Verify behavior with bash -l, bash -i, and echo $- checks.

Environment & Prerequisites

Prerequisites

  • Bash as your shell with a writable home directory.
  • The aliases and environment variables lab (you will persist those settings).
  • Know how to open a new login shell: bash -l or SSH in fresh.
  • No packages needed; this lab edits dotfiles only.

Step-by-Step Execution

1. Source .bashrc from .bash_profile

echo '[ -f ~/.bashrc ] && . ~/.bashrc' >> ~/.bash_profile

Expected output:

(line appended; verify with: tail -1 ~/.bash_profile)
[ -f ~/.bashrc ] && . ~/.bashrc

What this teaches: Login shells read .bash_profile but not .bashrc, so sourcing .bashrc from it unifies the two paths. The -f guard keeps it safe when .bashrc is absent.

2. Put customizations in .bashrc

echo "alias ll='ls -lAh'" >> ~/.bashrc && source ~/.bashrc

Expected output:

(alias appended and active immediately)
$ type ll
ll is aliased to `ls -lAh'

What this teaches: .bashrc is the home for interactive customizations (aliases, functions, prompts); sourcing it after editing applies changes without opening a new shell.

3. Confirm a login shell picks it up

bash -lc 'type ll'

Expected output:

ll is aliased to `ls -lAh'

What this teaches: bash -lc runs a login shell: if ll resolves, the .bash_profile to .bashrc chain works. This is the definitive test that graphical-terminal and SSH logins share your setup.

How It Works

Bash classifies each invocation along two axes: login versus non-login, and interactive versus non-interactive. A login shell (SSH session, console login, bash -l) reads /etc/profile then the first of ~/.bash_profile, ~/.bash_login, ~/.profile. A non-login interactive shell (a new terminal tab) reads ~/.bashrc only. Non-interactive shells read nothing unless BASH_ENV points at a file, with one documented exception: bash started by sshd to run a remote command reads ~/.bashrc even though it is non-interactive, which is why remote ssh commands sometimes see aliases that cron jobs do not.

This split exists for a reason: login shells set up the session once (PATH, environment), while every interactive subshell needs the interactive comforts (aliases, prompts, completion). The standard pattern, sourcing .bashrc from .bash_profile, merges the two so interactive settings exist in both contexts without duplication.

.profile is the POSIX fallback read when no bash-specific login file exists; keeping it minimal and bash-agnostic preserves compatibility with sh and dash. Debugging startup issues means tracing this order: echo markers or bash -x -l reveal exactly which files ran.

Validation & Testing

Open a non-login interactive shell and a login shell and confirm the same alias/environment exists in both. Pass criteria: customizations apply consistently across login and non-login shells with no duplication.

Troubleshooting

SymptomLikely causeFix
Aliases missing in new terminal tabsThey were defined in .bash_profile, which non-login shells skipMove interactive settings to .bashrc and source it from .bash_profile
PATH duplicated many times.bashrc prepends to PATH on every nested shellGuard with a case check: case ":$PATH:" in *:$HOME/.local/bin:*) ;; *) PATH=...;; esac
Changes to .bashrc had no effectThe running shell never re-read the fileRun source ~/.bashrc, or open a new shell
Login is slowA startup file runs something heavy (nvm, pyenv, network calls)Profile with bash -x -l and lazy-load expensive initializers
.bash_profile ignored entirely~/.bash_login exists and takes precedenceConsolidate into one login file; Bash reads only the first match
Scripts behave differently than the terminalNon-interactive shells skip .bashrcPut script-needed setup in BASH_ENV or export it from the caller
Advanced: Troubleshooting
  • Works in terminal, not SSH: SSH starts a login shell; ensure .bash_profile sources .bashrc.
  • Duplicated PATH: guard appends so re-sourcing doesn't stack entries.
  • cron lacks env: cron uses a minimal shell; set variables explicitly in the job.

Key Results

  • Established one canonical file for shell customization.
  • Eliminated environment drift between login and non-login shells.
  • Verified consistent behaviour across SSH and local terminals.
  • Prevented PATH duplication on repeated sourcing.

Key Takeaways

  • Login shells read .bash_profile; interactive non-login shells read .bashrc.
  • Source .bashrc from .bash_profile to unify both paths.
  • Interactive comforts go in .bashrc; session setup goes in the login file.
  • Guard PATH modifications against duplication in nested shells.
  • bash -lc 'type ll' is the definitive login-shell test.

Practice Challenges

Challenge 1: Trace the chain

Goal: add echo markers to each startup file, then open a login shell and a non-login shell and record which markers appear in each.

Hint: bash -l versus a new terminal tab.

Challenge 2: Idempotent PATH

Goal: rewrite your PATH setup so sourcing .bashrc ten times still leaves exactly one copy of each entry.

Hint: the case pattern on :$PATH: is the classic guard.