skip to content

bash (interactive shell)

Bash as the shell you sit in rather than the language you write: which startup file actually runs, how history and completion behave, and how readline shapes every keystroke. Interviewers ask because "why isn't my PATH set over SSH" is a real production question.

part ofCommand-line shellsoverview, primer and where to startread it →
on this pageshow

questions

5

In bash, which startup files does a login shell read compared with an interactive non-login shell, and why does almost every ~/.bash_profile contain a line that sources ~/.bashrc?

level: middleimportance: must knowfreq 72%

answer

  1. two axes, not one flag
  2. profile files versus the rc file
  3. first match wins among three
  4. one sourced line unifies both paths

basics

~10 s

A login bash shell reads /etc/profile and then the first existing of ~/.bash_profile, ~/.bash_login or ~/.profile. An interactive non-login shell reads ~/.bashrc instead. Sourcing ~/.bashrc from ~/.bash_profile makes both entry paths behave identically.

solid answer

~40 s

Bash picks its startup files from two independent properties: whether it is a *login* shell (started with `-l`, or with an argv[0] beginning with a dash, which is what `login` and `sshd` do) and whether it is *interactive*. A login shell reads `/etc/profile`, then the **first readable** file among `~/.bash_profile`, `~/.bash_login`, `~/.profile` — first match wins, the others are skipped — and `~/.bash_logout` on exit. An interactive non-login shell (a new terminal tab, a `screen`/`tmux` pane, `bash` typed inside a shell) reads `~/.bashrc` only, plus a system file such as `/etc/bash.bashrc` on Debian-family builds. Because the two paths share nothing, anything you want in every session would have to be duplicated. So `~/.bash_profile` conventionally does `[ -f ~/.bashrc ] && . ~/.bashrc`, making `~/.bashrc` the single place where prompt, aliases, `shopt` and completion live.

go deeper

for a junior

Know that ~/.bashrc is where aliases and prompt settings go and that it is read by new interactive shells, while ~/.bash_profile belongs to login sessions such as SSH. Be able to say why people source one from the other.

for a middle

Explain the login/interactive matrix and the exact file order, including that the three profile candidates are a first-match search. Say which kinds of settings are inherited by child shells and which are per-shell.

for a senior

Show how you diagnose a startup problem rather than guessing: --norc/--noprofile/--rcfile to isolate, bash -lxc exit to trace, and shopt login_shell plus $- to classify a live session. Explain why the split causes duplicated PATH entries.

for a principal

Own the fleet-wide policy: whether machine-wide settings belong in /etc/profile.d fragments or in configuration management, how to keep per-user rc files from silently breaking automation, and why a slow ~/.bashrc taxes every command run over SSH.

## Two questions bash asks at startup When bash starts it classifies itself along two axes, and the combination — not one flag — decides which files it reads. **Login shell.** Bash is a login shell when it is invoked with `-l` or `--login`, or when the program that executed it passed an `argv[0]` whose first character is a hyphen (you see this as `-bash` in `ps`). `login(1)` and `sshd` both do the hyphen trick, so a console login and an interactive `ssh host` session both give you a login shell. **Interactive shell.** Bash is interactive when it was started without a script argument and its input and output are attached to a terminal; it then prints a prompt and enables job control and line editing. Interactive shells have `i` in the special variable `$-`. The two are independent. You can have a login-and-interactive shell (SSH session), an interactive-non-login shell (a new terminal tab on most Linux desktops), a login-non-interactive shell (`bash -lc 'cmd'`), and a plain non-interactive shell (`bash script.sh`). ## What a login shell reads 1. `/etc/profile` — the system-wide file. On nearly every distribution it ends with a loop that sources `/etc/profile.d/*.sh`, which is where packages drop system-wide environment fragments. 2. Then the **first** of `~/.bash_profile`, `~/.bash_login`, `~/.profile` that exists and is readable. This is a first-match-wins search, not a chain: if you have both `~/.bash_profile` and `~/.profile`, only `~/.bash_profile` runs. This trips people who keep their environment in `~/.profile` (shared with `dash` or `sh`) and then create a `~/.bash_profile` for one bash-specific line — the rest of `~/.profile` silently stops running. 3. On exit, `~/.bash_logout` (and a system equivalent on some builds). `--noprofile` suppresses this whole step. ## What an interactive non-login shell reads Only `~/.bashrc`, and on distributions that compile bash with a system rc file (Debian and Ubuntu) `/etc/bash.bashrc` first. Red Hat-family systems achieve the same by having the stock `~/.bashrc` source `/etc/bashrc` itself. Such a shell never touches `/etc/profile` or `~/.bash_profile`. `--norc` suppresses it; `--rcfile FILE` substitutes a different file, which is the clean way to test a config without editing your own. ## The third case, briefly A non-interactive, non-login bash reads none of the above. It expands the value of `BASH_ENV` and, if that names a readable file, sources it. This is why environment set only in `~/.bashrc` is missing from `cron` jobs and from `ssh host cmd`. ## Why the source line exists The historical split assumed a login session started once and spawned everything else, so the profile set exported variables (inherited by every child) and `~/.bashrc` set per-shell things that are *not* inherited: aliases, shell functions, `shopt` settings, the prompt, readline bindings, completion specs. Modern desktops broke the assumption — a terminal emulator typically starts an interactive non-login shell, so the profile may never run in a whole day of work. The conventional fix is one line at the end of `~/.bash_profile`: ```bash [ -f ~/.bashrc ] && . ~/.bashrc ``` Now both entry paths converge on `~/.bashrc`, and you maintain one file. The complementary discipline is to keep `~/.bashrc` fast and side-effect-free at the top, because it now runs for every shell. ## Which setting belongs where - Exported environment (`PATH`, `EDITOR`, `LANG`): the profile is the classic home, since children inherit it and it need only run once. Putting it in `~/.bashrc` also works but re-runs on every nested shell, which is how `PATH` ends up with duplicate entries. - Aliases, functions, prompt, `shopt`, `complete` specs, readline `bind` commands: `~/.bashrc`, always. They are per-shell state and are not inherited. ## Telling which shell you are in ```bash shopt -q login_shell && echo "login shell" case $- in *i*) echo "interactive";; *) echo "not interactive";; esac ``` `echo $0` showing `-bash` is the other classic tell. When startup behaviour is mysterious, `bash -lxc exit` traces exactly which files a login shell executes, in order.

  • How would you check, from inside a running shell, whether it is a login shell and whether it is interactive?
    `shopt -q login_shell` returns success for a login shell, and bash sets `login_shell` at startup only. For interactivity, test the special variable `$-` for an `i` flag: `case $- in *i*) ...;; esac`. Informally, `echo $0` printing `-bash` also indicates a login shell, because the leading hyphen in argv[0] is how the parent process requested one.
  • A user keeps their environment in ~/.profile, then adds a small ~/.bash_profile. Their settings stop applying. Why?
    A login bash reads only the first readable file among `~/.bash_profile`, `~/.bash_login` and `~/.profile` — it is a first-match search, not a sequence. Creating `~/.bash_profile` makes bash stop reading `~/.profile` entirely. The fix is to source it explicitly from `~/.bash_profile`, keeping `~/.profile` as the portable, sh-compatible part.
  • How do you test a change to a startup file without breaking your current session?
    Start a throwaway shell with the file you want: `bash --rcfile /tmp/test.bashrc -i` for rc changes, or `bash --noprofile --norc` for a clean baseline. To see what actually runs during a login, `bash -lxc exit` prints an execution trace of the profile chain. None of these disturb the shell you are already sitting in.

saying these in an interview costs you the question

  • Says a login shell reads .bash_profile and then .bashrc automatically
  • Believes all three of .bash_profile, .bash_login and .profile are read
  • Claims a new terminal tab always runs /etc/profile
  • Thinks aliases defined in a parent shell are inherited by children
  • Assumes editing .bashrc changes shells that are already running

context

open as a page

Bash edits the command line through the GNU Readline library. What does pressing Ctrl-R at a bash prompt do, and where do you make a change to bash's keybindings or editing mode permanent?

level: juniorimportance: should knowfreq 46%

basics

~20 s

Ctrl-R starts an incremental reverse search through the current session's command history: keep typing to narrow it, press Ctrl-R again for older matches, Enter to run. Persist keybinding and editing-mode changes in ~/.inputrc, or with bind in ~/.bashrc.

open as a page

You keep two bash terminals open all day, and commands typed in the first are missing from history after the second one exits. How does bash write its history file, and which settings stop concurrent sessions from clobbering each other?

level: middleimportance: should knowfreq 52%

basics

~20 s

Each bash session keeps history in memory and writes it out when the shell exits, overwriting HISTFILE by default — so the last shell to exit wins. shopt -s histappend makes sessions append instead, and a history -a in PROMPT_COMMAND shares lines immediately.

open as a page

A command works when you log in to a Linux host and type its name, but `ssh host mytool` and the same command from a crontab both fail with "command not found". Explain this in terms of which bash startup files each of those runs actually reads.

level: seniorimportance: should knowfreq 58%

basics

~20 s

The PATH entry lives in a startup file those invocations never read. ssh host mytool runs a non-interactive, non-login shell, and cron runs the command with a minimal environment, so neither ~/.bash_profile nor (in practice) ~/.bashrc applies.

open as a page

You ship a command-line tool called `deploytool` and want `deploytool <TAB>` to complete its subcommands in bash. Which bash builtins and shell variables implement that, and where does the completion script get installed?

level: middleimportance: nice to knowfreq 30%

basics

~20 s

Bash's complete builtin registers how a command completes. A fixed word list is complete -W "start stop status" deploytool; anything context-sensitive uses complete -F _deploytool deploytool, where the function fills the COMPREPLY array, usually from compgen. Install it in the bash-completion completions directory.

open as a page