6.5 Command History: history, Ctrl+R, HISTSIZE, HISTFILESIZE
Right, let’s talk about your shell’s memory. It’s not just a list of stuff you’ve typed; it’s your most powerful productivity tool, a personal log of your every triumph and catastrophic typo. Mastering it is the difference between feeling like a wizard and feeling like you’re constantly retyping the same seven commands. We’re going to crack it open.
Your Digital Elephant: The history Command
The most straightforward way to access your history is, unsurprisingly, the history command. Go on, run it. I’ll wait.
$ history
1 ls -la
2 cd Projects/
3 vim important_script.sh
4 ./important_script.sh
... (and so on, for a surprisingly long time)
See that? Your shell remembers everything. This isn’t just for nostalgia; it’s raw material. The most common use is re-running a command by its number. See that ./important_script.sh at number 4? You can re-run it with a bang (!) followed by the number:
$ !4
Boom. Command executed again. But let’s be honest, you’re not going to count lines every time. The real power is in searching.
Reverse-i-Search: Your New Best Friend (Ctrl+R)
This is the killer feature. If you’re not using this, you’re working too hard. Press Ctrl+R and start typing any part of a previous command. Your shell will instantly find the most recent match and show it to you.
(reverse-i-search)`ssh': ssh myuser@super-important-server.com
Hit Enter to execute it immediately, or press the right or left arrow key to drop the command into your prompt so you can edit it first. This last part is crucial. Found the apt-get install command from three days ago but need to change the package name? Ctrl+R, type install, left arrow, edit. Done.
Now, the absurd part? If you keep pressing Ctrl+R, it cycles backwards through older matches. To cycle forwards? There isn’t a standard key for it. It’s madness. In bash, you’re out of luck. In zsh, you can use Ctrl+S to cycle forward, but chances are your terminal has Ctrl+S captured for something else (like “stop output”). It’s a classic case of terminal emulators and shells having a decades-long feud they forgot to tell you about. To fix it, you might need to run stty -ixon to disable that behavior. See? Trench life.
Taming the Beast: HISTSIZE and HISTFILESIZE
Your shell’s history isn’t infinite. It has two limits, and most default setups are pathetically small. These are controlled by environment variables:
HISTSIZE: This is the number of commands held in memory for your current session. This is your “working set.”HISTFILESIZE: This is the number of commands persisted to the history file (usually~/.bash_historyor~/.zsh_history) when you close the shell, so they’re available in your next session.
The default is often 500 or 1000. This is a tragedy. You are not a peasant. You deserve a proper historical record. Here’s how you fix this permanently. Open your ~/.bashrc (for bash) or ~/.zshrc (for zsh) and add these lines:
# Make my history file glorious and huge
HISTSIZE=100000
HISTFILESIZE=200000
Why a bigger HISTFILESIZE? Because your session history (HISTSIZE) gets written to the file on exit, but you can also have other shells append to the same file. A larger file size ensures you don’t lose ancient history just because you opened a few tabs this month.
Avoiding the Pitfalls: History Settings
Now, the designers made some… choices. By default, if you have multiple shell sessions open, the last one to exit overwrites the history file of all the others. It’s a braindead default. You’ll have a terminal window open for a week, run a thousand commands, then exit and watch them all vanish because another tab you opened five seconds ago closed first and wrote its paltry history over yours.
Fight back. You need to configure your history to append in real-time or at least ensure sessions append to the file, rather than overwriting. For bash, add this to your ~/.bashrc:
# Append to the history file, don't overwrite it
shopt -s histappend
# Attempt to save all lines of a multi-line command in the same history entry
shopt -s cmdhist
# Immediately add commands to the history file, not just at shell exit.
# This helps a bit with multi-session history, but isn't perfect.
PROMPT_COMMAND="history -a; $PROMPT_COMMAND"
For zsh, it’s generally better behaved, but you should set this in your ~/.zshrc to use a more robust format and avoid duplicates:
# Use the extended history format (timestamp)
setopt EXTENDED_HISTORY
# Add commands as they are typed, not on shell exit
setopt INC_APPEND_HISTORY
# Share history between all sessions (this is the magic)
setopt SHARE_HISTORY
# Do not write duplicate events to the history file
setopt HIST_IGNORE_DUPS
The zsh implementation is, frankly, more sane. The SHARE_HISTORY option means a command run in one terminal is instantly available in another via Ctrl+R. It’s how it should have always worked.
The Nuclear Option: HISTIGNORE and HISTCONTROL
Maybe you don’t want the shell to remember everything. Maybe you don’t want it saving commands that start with a space, or repeated duplicates, or your ls commands cluttering up the valuable real estate. You can control this.
HISTCONTROL (in bash) is your friend. A common and excellent setting is:
# ignorespace: ignore commands starting with a space
# ignoredups: don't save consecutive duplicates
# erasedups: remove *all* previous duplicates from history, not just consecutive ones
HISTCONTROL=ignorespace:ignoredups:erasedups
The ignorespace is a fantastic trick for secrets. Prefix a command with a space, and it simply won’t show up in your history.
$ export SUPER_SECRET_API_KEY="abc123" # This won't be saved. Shhh.
For more complex filtering, bash has HISTIGNORE and zsh has HIST_IGNORE, which let you specify patterns to exclude (e.g., HISTIGNORE="ls:cd:exit"). Use them sparingly. You might think you’ll never need to search for that cd command, until you’re trying to remember the path to that obscure directory you found three weeks ago. Your history is a log. Treat it with respect.