Every command you type into Bash gets remembered, and learning to actually use that history — rather than just pressing the up arrow a dozen times — is one of those skills that quietly saves hours over time. Bash’s history system is far deeper than most people realize: fuzzy re-execution, search-as-you-type, targeted deletion, cross-session persistence, and shortcuts for repeating or modifying past commands without retyping them.
This guide covers the full range of Bash history features, from the basics to the tricks power users rely on daily.
Where Bash History Lives
By default, Bash keeps your command history in memory during a session and writes it to a file — typically ~/.bash_history — when the shell exits. Several environment variables control this behavior:
echo $HISTFILE # path to the history file, usually ~/.bash_history
echo $HISTSIZE # number of commands kept in memory for the session
echo $HISTFILESIZE # number of commands kept in the history file
Viewing History
# Show the full command history with line numbers
history
# Show the last 20 commands
history 20
# Search history for lines containing "git"
history | grep git
Each line is numbered, and that number is your key to re-running any past command instantly.
Re-Running Past Commands
# Re-run command number 482
!482
# Re-run the last command
!!
# Re-run the last command that started with "git"
!git
# Re-run the last command containing "commit" anywhere in it
!?commit
!! is one of the most useful shortcuts in daily use — for example, forgetting sudo:
apt update
# Permission denied
sudo !!
# runs: sudo apt update
Searching History Interactively
The single most valuable history feature is reverse incremental search, triggered with Ctrl+R:
(reverse-i-search)`git': git commit -m "fix login bug"
Start typing any part of a past command, and Bash finds the most recent match in real time. Press Ctrl+R again to cycle to the next older match, Enter to run the found command, or Esc/arrow keys to edit it before running.
To search forward (toward more recent commands) after having gone backward, use Ctrl+S (note: this may require disabling terminal flow control with stty -ixon first, since Ctrl+S is historically bound to pausing terminal output).
Editing and Reusing Parts of Previous Commands
# Reuse the LAST argument of the previous command
mkdir new_folder
cd !$
# runs: cd new_folder
# Reuse ALL arguments of the previous command
grep "error" server.log logfile2.log
wc -l !*
# runs: wc -l server.log logfile2.log
# Substitute a word in the last command and re-run it
echo "helo world"
^helo^hello
# runs: echo "hello world"
!$ and !* are quick shortcuts for reusing the arguments of your previous command without retyping them — extremely handy when you’re doing several operations on the same file or files in a row.
History Expansion Reference
| Shortcut | Meaning |
|---|---|
!! | Last command |
!n | Command number n from history |
!-n | The nth command back from the current one |
!string | Most recent command starting with string |
!?string? | Most recent command containing string anywhere |
!$ | Last argument of the previous command |
!* | All arguments of the previous command |
!^ | First argument of the previous command |
^old^new | Re-run last command, replacing “old” with “new” |
Deleting Specific History Entries
# Delete entry number 245
history -d 245
# Clear the entire history for the current session
history -c
# Also remove the persisted history file
history -c && rm -f ~/.bash_history
This is especially relevant right after accidentally typing a password or API key directly into the terminal — deleting the specific entry (or the whole session’s history) prevents it from being written to disk.
Preventing a Command from Being Saved to History
Prefix a command with a space (requires HISTCONTROL to include ignorespace, which is the default on most systems):
export API_KEY="super-secret-value"
That leading space keeps this specific command out of history entirely, which is a simple, effective habit for anything sensitive typed directly on the command line.
Useful HISTCONTROL and Related Settings
Add these to ~/.bashrc to shape how history behaves:
# Ignore duplicate consecutive commands and commands starting with a space
export HISTCONTROL=ignoreboth
# Increase history size significantly
export HISTSIZE=10000
export HISTFILESIZE=20000
# Add a timestamp to each history entry
export HISTTIMEFORMAT="%F %T "
# Append to history file rather than overwriting it (important for multiple open terminals)
shopt -s histappend
# Save each command to history immediately, not just at shell exit
export PROMPT_COMMAND="history -a; $PROMPT_COMMAND"
How histappend and the PROMPT_COMMAND trick work together: by default, each Bash session only writes its history to disk when it closes, and does so by overwriting the file — meaning if you have two terminals open, the one that closes last wins and the other’s commands are lost. shopt -s histappend changes closing behavior to append instead of overwrite, and adding history -a to PROMPT_COMMAND writes each command to the file immediately after it runs, rather than waiting for the shell to exit at all. Together, they make history sharing across multiple simultaneous terminal sessions reliable.
Real-World Use Cases
Finding a Complex Command You Ran Weeks Ago
history | grep -i "rsync"
With HISTTIMEFORMAT set, this also shows exactly when you ran it — useful for reconstructing “what did I do to fix this last time.”
Auditing What Was Run on a Shared Server
# Combined with HISTTIMEFORMAT, gives a timestamped audit trail
cat ~/.bash_history
For genuine auditing purposes on shared/production systems, dedicated session-recording tools (like script, auditd, or a bastion host with full logging) are far more reliable than relying on .bash_history, since history files can be edited or cleared by the user.
Quickly Repeating a Build/Test Cycle
make build && ./run_tests.sh
# ... fix something ...
!!
Rapid File Operations Using Argument Reuse
touch report.txt
vim !$
chmod 644 !$
Best Practices
- Set a generous
HISTSIZE/HISTFILESIZE— command history is cheap to store and often valuable to search later. - Enable
HISTTIMEFORMATso you can see when you ran something, not just what. - Use
Ctrl+Ras your default way to find and re-run commands rather than scrolling with the up arrow, especially for anything more than a few commands back. - Enable
histappendand thePROMPT_COMMANDtrick if you regularly work across multiple terminal tabs or panes — otherwise you’ll lose commands from sessions that closed “out of order.” - Prefix sensitive one-off commands with a leading space instead of typing secrets directly and hoping to remember to clean up afterward.
Security Considerations
~/.bash_historyis a plain text file readable by the file’s owner — never type passwords, API keys, or tokens directly as command-line arguments if you can avoid it, since they’ll persist in history indefinitely otherwise.- Prefer environment variables sourced from a restricted-permission file, or interactive prompts (
read -s), over passing secrets as visible arguments. - On shared or multi-admin systems, be aware that
.bash_historycan reveal sensitive operational details (server names, internal paths, deployment steps) — treat it with the same care as any other file that might leak infrastructure details. - Remember that
history -cand deleting.bash_historyonly clear local traces — if a command was also logged elsewhere (shell audit logging, a bastion host, terminal session recording), it isn’t actually erased from those systems.
Optimization Tips
- Use
Ctrl+Rcombined with a decentHISTSIZE— a small history size limits how far back you can effectively search, which defeats much of the value of keeping history at all. - For very long-running servers with heavy interactive use, periodically archive very old
.bash_historycontent rather than letting a single file grow unbounded, if disk space or slow load times on shell startup become a concern (though this is rarely an issue at typicalHISTFILESIZEvalues). - Combine
history | grepwithawkfor quick statistics, e.g., finding your most frequently used commands:history | awk '{print $2}' | sort | uniq -c | sort -rn | head -10
Troubleshooting Common Issues
Problem: Commands from one terminal don’t show up in another open terminal. This is expected default behavior — each session only writes history on exit, and by default overwrites rather than merges. Enable histappend and the PROMPT_COMMAND trick described above to share history live across sessions.
Problem: !! or !$ doesn’t seem to work as expected. History expansion is disabled in some non-interactive contexts and can behave unexpectedly inside scripts — this feature is designed for interactive use. Confirm set -H (history expansion) hasn’t been disabled, which it is by default in some non-login shell configurations.
Problem: Sensitive command still appears in history despite a leading space. Check that HISTCONTROL actually includes ignorespace or ignoreboth — without it, the leading-space trick has no effect, and every command is recorded regardless.
Problem: History seems to be missing entries after a crash or forced terminal close. Since history is normally only flushed to disk at clean shell exit, an abrupt close (crash, kill -9 on the shell itself) can lose recent commands. The PROMPT_COMMAND trick that writes history after every command largely eliminates this risk.
Common Mistakes
- Typing secrets directly as command arguments, leaving them permanently in plaintext history.
- Not enabling
histappend, causing lost commands when working across multiple terminal sessions. - Relying only on the up arrow instead of
Ctrl+R, wasting time scrolling through long histories. - Assuming
history -cfully erases a command from every system it may have touched (like centralized audit logging). - Setting
HISTSIZEtoo small, limiting how far back useful search and recall can reach.
Frequently Asked Questions
How do I permanently increase my history size? Add export HISTSIZE=10000 and export HISTFILESIZE=20000 (or larger values) to ~/.bashrc, then reload with source ~/.bashrc.
What’s the difference between HISTSIZE and HISTFILESIZE? HISTSIZE controls how many commands are kept in memory during the current session; HISTFILESIZE controls how many are retained in the history file on disk across sessions.
Can I remove a single embarrassing or sensitive command from history without clearing everything? Yes — find its line number with history, then run history -d <number> to remove just that entry.
Why does Ctrl+R sometimes show an old command I don’t want, and how do I search further back? Press Ctrl+R again while already in a search to cycle backward to the next older match, rather than starting a new search each time.
Summary
Bash’s history system is far more than a scrollback log — with Ctrl+R search, !!/!$/!* expansion shortcuts, targeted deletion, and settings like histappend and HISTTIMEFORMAT, it becomes a genuinely powerful tool for working faster and keeping a searchable record of what you’ve done. A few minutes configuring ~/.bashrc properly, plus building the habit of using Ctrl+R instead of the up arrow, pays off almost immediately in day-to-day terminal work.
References
- GNU Bash Manual — Bash History Facilities: https://www.gnu.org/software/bash/manual/bash.html#Bash-History-Facilities
- GNU Bash Manual — History Expansion: https://www.gnu.org/software/bash/manual/bash.html#History-Interaction
- GNU Bash Manual — The Shopt Builtin: https://www.gnu.org/software/bash/manual/bash.html#The-Shopt-Builtin
- GNU Readline Library Manual: https://www.gnu.org/software/bash/manual/html_node/Readline-Interaction.html