Skip to content
woolly.me
← Workbench

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

theme

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.