.bashrc series

History Is Not Just up-arrow

HISTSIZE, HISTCONTROL, PROMPT_COMMAND, and the multi-tab problem

May 17, 2026 ~6 min
bashhistoryproductivity

The default bash history settings in every modern Linux distribution are fine. Fine for someone using bash for fifteen minutes a day. Not for an engineer who lives in it.

Here's what the Mcaster1 fleet-wide environment file does to bash history:

HISTSIZE=100000
HISTFILESIZE=200000
HISTCONTROL=ignoreboth:erasedups
HISTTIMEFORMAT='%F %T  '
HISTIGNORE='ls:la:ll:cd:pwd:exit:history:clear:[bf]g'
shopt -s histappend
shopt -s cmdhist
PROMPT_COMMAND='history -a; '"${PROMPT_COMMAND:-}"

Eight lines. Let's walk each one and what it actually buys you.

HISTSIZE=100000 and HISTFILESIZE=200000

Default is 500 / 500. Five hundred commands is what you type before lunch. The reason the default is so small is historical — bash was written when memory was expensive and "history" meant a dozen invocations. Today there's no excuse. 100,000 in-memory history entries cost you maybe 8 MB of RAM. Treat your shell history as a personal research database.

HISTCONTROL=ignoreboth:erasedups

Three behaviors combined: ignorespace (commands starting with a space don't get recorded — useful for typing one-off secrets), ignoredups (don't record a command identical to the previous one), and erasedups (when a duplicate is added, remove the earlier copy of the same command).

The practical effect: your up-arrow stops cycling through five copies of git status. Your Ctrl-R search lands on the most recent unique invocation of a command instead of fifty stale copies.

HISTTIMEFORMAT='%F %T '

Timestamps your history. Run history and every line now has a date and time. This is invaluable when you're debugging "what did I run yesterday during the outage?" or filling out a postmortem timeline. The timestamps are stored in ~/.bash_history as Unix epoch comments interleaved with commands — bash reads them back as long as HISTTIMEFORMAT is set.

HISTIGNORE — clean the noise

ls, cd, pwd, clear, exit — you type these a hundred times an hour. They don't deserve a history slot. The HISTIGNORE pattern excludes them at write time, leaving your history full of the commands you'd actually want to look up later.

shopt -s histappend — don't clobber your own history

Without this, when a shell exits it overwrites ~/.bash_history with its in-memory history. If you have three tabs open and one exits, the other two will eventually overwrite its contributions. histappend changes the exit behavior to append. Necessary for any multi-shell workflow.

shopt -s cmdhist — multi-line commands as one entry

By default, a multi-line command (a function definition, a heredoc, an awk one-liner with embedded newlines) is stored as multiple history entries — one per line. cmdhist stores it as a single entry. When you up-arrow back to it, you get the whole command on one line, ready to edit.

PROMPT_COMMAND='history -a; ...' — the multi-tab fix

This is the trick almost nobody knows.

PROMPT_COMMAND is a string bash executes before each prompt. Setting it to history -a means: every time you hit Enter, flush this shell's history additions to disk immediately.

Combined with histappend, this fixes the multi-tab problem entirely. Open three terminals. Run foo in tab 1. Switch to tab 2, hit up-arrow — foo is there if you also have shopt -s histread set, OR if you're starting a new shell. Without history -a in PROMPT_COMMAND, tab 2 wouldn't see foo until tab 1 exits.

The eight lines above are the difference between bash history being a useless 500-entry circular buffer and bash history being an actual research tool you can grep, audit, and reason about.

Next: Color-Coding Your Prompt So You Don't rm -rf Production.

All .bashrc articles