kmail.at
contact

elsewhere

EnglishEN
← learning

linux · difficulty ◆

history — your own command logbook

That perfect 12-flag command you ran last week is one Ctrl-R away — you just never learned to ask.

Ctrl-R won't just find the command — it finds the one you forgot you even typed.

AI-generated draft · reviewed and editedThis text was generated with AI as a first draft, then reviewed and edited by the author, who holds editorial responsibility for it.

2026-09-20 · 5 min read

$ history

It's more than a diary

Every interactive bash session appends each line you type to an in-memory list, and on exit writes it to ~/.bash_history. The `history` command prints that list, numbered; `!42` re-runs entry 42; `!grep` re-runs the last command starting with grep. The real unlock is that this isn't a read-only archive — HISTFILE, HISTSIZE, and HISTCONTROL shape what gets remembered and what's silently skipped.

Search is the superpower

The single most useful terminal habit is Ctrl-R: press it, type two or three characters, and bash walks you back through every matching command you've ever run. Tap Ctrl-R again to go further back, Enter to run. Combined with `!$` (last argument of the previous command) and `!!` (the whole previous command), you can replay a long pipeline without touching the keyboard twice.

One history per shell — and how to merge or scrub them

Each shell keeps its own in-memory list and writes it out only when something tells it to, which is why two open terminals can step on each other. The behaviour is the `histappend` shell option: set it and bash appends this session's list to $HISTFILE when the shell exits; leave it unset and bash rewrites the file from the list instead, so the other window's commands disappear. Ubuntu's default ~/.bashrc sets it, two lines below HISTCONTROL. Three builtins move the lines by hand: `history -a` appends this session's new lines to the file immediately (verified here — the file had them while the shell was still running), `history -n` pulls in whatever other shells have appended since the last check, and `history -w` writes the current list over the file. The idiom that ties them together is `PROMPT_COMMAND='history -a'` in ~/.bashrc: PROMPT_COMMAND runs before every prompt, so each command is on disk before you type the next one and the other terminal can pick it up with `history -n`. For the opposite problem — a history that holds too much — HISTIGNORE takes colon-separated globs matched against the whole line (`HISTIGNORE='ls:cd:history*'`), HISTCONTROL=erasedups removes any earlier copy of a command before saving the new one, and HISTTIMEFORMAT stamps every entry with the time it ran.

Example

$ $ history | tail -6
  123  cd /var/log
  124  du -sh *
  125  find /home -name '*.conf' -size +1M
  126  journalctl -u nginx --since today --no-pager
  127  sudo apt upgrade
  128  history | tail -6

The numbered list. The numbers are the keys: `!125` re-runs that exact find one-liner.

$ $ !126
journalctl -u nginx --since today --no-pager
Sep 20 09:00:02 srv nginx[1282]: 10.0.0.7 - - [20/Sep/2026:09:00:02] "GET /health HTTP/1.1" 200 18

!<n> re-executes command n. Bash echoes the expansion first, then runs it — no ambiguity about what you're firing.

$ $ tar czf backup-2026.tar.gz src/
$ du -sh !$
du -sh backup-2026.tar.gz
1.2M    backup-2026.tar.gz

!$ expands to the last word of the previous command — here the archive filename. `!!` expands to the entire previous line; `sudo !!` reruns it as root.

$ $ fc -l 20 25
20	 ls -la
21	 cd projects
22	 git status
23	 git log --oneline -5
24	 cat README.md
25	 npm test

fc lists a window of history between two numbers — bash 5.2 prints the number, a tab, then the line. Useful for grepping before you commit a command to a script, and `fc -ln -3` drops the numbers, which is the form you want when you paste the result in. fc is also how you edit and re-run a previous command in your editor.

$ $ export API_KEY=hunter2
$ history 2
    1  history 2

The leading space on the first line is the whole trick: with HISTCONTROL=ignorespace — or ignoreboth, which Ubuntu's default .bashrc sets — a line that begins with a space never reaches the history list, so the key is nowhere in ~/.bash_history. `history 2` shows only itself. Two related values finish the job: ignoredups drops a line that repeats the previous entry (two `echo dup` in a row leave one entry, verified here), and erasedups removes any earlier copy before saving the new one.

Common flags

-c
Clear the history list. The file is only rewritten when the shell exits, and only if histappend is off; with histappend set (Ubuntu's default .bashrc turns it on) the old lines stay in ~/.bash_history, so use `history -c && history -w` when you actually mean to scrub it.
-d offset
Delete the history entry at the given numbered position; a negative offset counts back from the end, so `history -d -1` drops the last command.
-a
Append the current-session lines to HISTFILE immediately, instead of waiting for exit
-w
Write the in-memory history to the file now
-n
Read new lines since last time from the file, without printing
-r
Read the file and load its lines into the current session's list
-p
Perform history expansion on args and print the result, without executing

History

The ! that predates bash

History expansion with `!` came from the C shell (csh), written by Bill Joy at Berkeley and first distributed with 2BSD in 1978 — history and command-line editing were its headline features, and `!!` and `!$` are its direct descendants. bash kept the whole family of bang shortcuts and layered readline's search keys on top: Ctrl-R for reverse incremental search, and Up-arrow prefix search once you bind it (see the tip below). Old-timers still argue over which is faster — the `!$` 'last argument' trick is straight csh lineage either way.

The history of history

Before `history`, Unix had no persistent memory of your commands — you retyped everything, every session. The interactive command history was one of the things that made Unix shells feel alive. bash has written ~/.bash_history from its first release in 1989, and the caps still decide how much survives: `man bash` sets HISTSIZE's default at 500 commands for the running session and gives HISTFILESIZE the same value by default. The 1000 / 2000 pair most people see is not bash's own setting but their distribution's — Ubuntu's /etc/skel/.bashrc ships `HISTSIZE=1000` and `HISTFILESIZE=2000`, which is why ~/.bashrc carries the same two lines.

Fun facts

Pros & cons

pros

  • + Re-run any prior command with !<number> or !foo — no retyping long pipelines
  • + Ctrl-R turns your entire memory of the terminal into a searchable, failure-proof index
  • + !$ and !! let you chain onto the last command's output or escalate it with sudo in one keystroke

cons

  • − Plaintext secrets and commands sit in ~/.bash_history unless you filter or scrub them
  • − Each shell has its own in-memory copy; merging across sessions needs -a or setting histappend
  • − Default dedup is off, so a repeated command can pad the file with thousands of near-identical lines

Takeaways

  1. 1Re-run without typing: `!42` by number, `!npm` by prefix, `!!` for the last command, `!$` for its last argument
  2. 2Ctrl-R is the habit that pays: press it, type a fragment, Enter to execute the match
  3. 3Protect secrets with a leading space and HISTCONTROL=ignorespace — or scrub the file before it bites
  4. 4Inspect before replaying: `fc -l 20 30` shows the numbered window you're about to reuse
  5. 5Persist across sessions reliably by exporting histappend and raising HISTFILESIZE

Related commands