Workbench · reference sheet
.bashrc conventions
The split: ~/.bash_profile runs once per login, ~/.bashrc runs in every interactive shell, and one sourcing line joins them. Everything below assumes current bash; legacy shows up only where you still have to adapt around it.
Last updated
Which file runs
~/.bashrc
every interactive shell: aliases, functions, prompt
~/.bash_profile
login shells only: ssh sessions, console logins
[ -f ~/.bashrc ] && . ~/.bashrc
in .bash_profile — one config serves both
[[ $- != *i* ]] && return
top of .bashrc: bail unless interactive (protects scp / rsync)
exec bash
reload after an edit; re-sourcing repeats PATH edits
ssh hands you a login shell; a terminal window usually doesn’t. The sourcing line makes the difference invisible.
History worth keeping
HISTSIZE=-1
unlimited in-memory history (bash 4.3+)
HISTFILESIZE=-1
never truncate the file
HISTCONTROL=ignoreboth:erasedups
skip dupes and space-prefixed commands
shopt -s histappend
append on exit instead of overwriting
PROMPT_COMMAND='history -a'
write each command to disk as you go
HISTTIMEFORMAT='%F %T '
timestamps in history output
Ctrl-r
search all of it — the payoff
A leading space keeps a command out of history entirely; that is the ignorespace half of ignoreboth.
shopt quality of life
shopt -s autocd
type a directory path, land in it
shopt -s cdspell dirspell
fix minor typos in cd and completion
shopt -s globstar
**/*.tf recurses; off by default
shopt -s checkjobs
second chance before exiting over running jobs
shopt
list what is already on
If an old rc sets checkwinsize, bash 5 turned it on by default — that line can go.
Aliases & functions
alias ls='ls --color=auto'
color from GNU coreutils
alias grep='grep --color=auto'
highlight matches
alias ll='ls -lah'
the one everyone has
mkcd() { mkdir -p "$1" && cd "$1"; }
needs an argument: function, not alias
\ls or command ls
run the real thing, alias skipped
type ll
what a name actually resolves to
Aliases are text substitution. Past one argument or one pipe, write a function.
Split it up
~/.bash_aliases
Debian and Ubuntu source it out of the box
~/.bashrc.d/
Fedora’s convention: one topic per file
for f in ~/.bashrc.d/*.sh; do . "$f"; done
the whole mechanism
~/.bashrc.local
machine-specific overrides, kept out of git
Track the main file in your dotfiles repo; the .local file holds what differs per machine. API keys belong in neither.
PATH & environment
~/.local/bin
where your own scripts go; most distros already have it on PATH
export EDITOR=vim
git, crontab, and systemctl edit all follow it
printf '%s\n' "${PATH//:/$'\n'}"
read PATH one entry per line
Guard additions so re-sourcing adds them once: case ":$PATH:" in *":$HOME/bin:"*) ;; *) PATH="$HOME/bin:$PATH" ;; esac
Prompt
PS1='\u@\h \w \$ '
user, host, cwd, and # when root
\[ … \]
wrap every color code or line-wrapping math breaks
__git_ps1
branch in the prompt; ships with git as git-prompt.sh
PROMPT_COMMAND
runs before each prompt; an array since bash 5.1
eval "$(starship init bash)"
the buy-not-build option
Tool hooks
eval "$(direnv hook bash)"
per-directory env vars
eval "$(zoxide init bash)"
z proj instead of the full cd path
eval "$(mise activate bash)"
language versions per project
eval "$(fzf --bash)"
fuzzy Ctrl-r and Ctrl-t (fzf 0.48+)
command -v direnv >/dev/null &&
prefix each hook: no errors where the tool is missing
Hooks go last in .bashrc — most chain onto PROMPT_COMMAND and expect the prompt already settled. Older fzf installs sourced ~/.fzf.bash instead.
Keep it fast
time bash -ic exit
startup cost; you pay it in every new terminal and tmux pane
PS4='+$EPOCHREALTIME ' bash -xic exit
timestamped trace to find the slow line (bash 5+)
nvm.sh
the classic offender — lazy-load it, or switch to fnm / mise
Belongs in ~/.inputrc
set completion-ignore-case on
Tab matches regardless of case
"\e[A": history-search-backward
type a prefix, arrow up through matches
set show-all-if-ambiguous on
choices on the first Tab, not the second
set colored-stats on
color the completion listing like ls
These are readline settings, so they live in ~/.inputrc — and python, psql, and everything else built on readline picks them up too.