Bash remembers the commands you type. Used well, history saves endless retyping. You can find that long rsync command from last week in two seconds, or rerun the last command with sudo. It also tells you what someone did on a server, as long as it’s configured properly.

View history

history            # all saved commands, numbered
history 20         # the last 20
history | grep rsync

History is kept in memory during a session and written to ~/.bash_history when the shell exits.

Search: Ctrl+R

Press Ctrl+R and start typing. Bash shows the most recent matching command.

  • Press Ctrl+R again for older matches.
  • Enter runs the match. → or Esc puts it on the line for editing.
  • Ctrl+G cancels.

This is the single most useful history feature. Tools like fzf give Ctrl+R a fuzzy, full-list search.

Rerun commands with ! (history expansion)

Syntax Meaning
!! The previous command
sudo !! Rerun the previous command with sudo
!123 Command number 123 from history
!-2 Two commands ago
!rsync Most recent command starting with rsync
!?backup? Most recent command containing backup
!$ Last argument of the previous command
!* All arguments of the previous command
^old^new Rerun the previous command, replacing old with new

Examples:

apt install nginx
sudo !!                          # sudo apt install nginx

mkdir -p /srv/app/releases
cd !$                            # cd /srv/app/releases

systemctl status nginx
^status^restart                  # systemctl restart nginx

To see what a ! expression expands to before running it, add :p, as in !rsync:p. Or enable shopt -s histverify so expansions are shown for editing first.

Alt+. (or Esc then .) inserts the previous command’s last argument at the cursor. It’s often more convenient than !$.

Configure history

Add these to ~/.bashrc (or /etc/profile.d/history.sh for all users):

HISTSIZE=50000                  # commands kept in memory
HISTFILESIZE=100000             # lines kept in ~/.bash_history
HISTTIMEFORMAT='%F %T  '        # timestamps in `history` output
HISTCONTROL=ignoreboth          # skip duplicates and commands starting with a space
HISTIGNORE='ls:ll:pwd:clear:history'
shopt -s histappend             # append to the file instead of overwriting it
PROMPT_COMMAND="history -a${PROMPT_COMMAND:+; $PROMPT_COMMAND}"   # save after every command

Why each matters on servers:

  • Timestamps let you answer “when did someone run that?”
  • histappend + history -a stops several SSH sessions from overwriting each other’s history, and saves commands even if the session is killed.
  • ignoreboth keeps the history clean, and gives you a way to keep a command out of it (see below).

Run source ~/.bashrc to apply.

Keep secrets out of history

Commands with passwords or tokens end up in ~/.bash_history in plain text:

mysql -u root -pSuperSecret       # bad: saved in history and visible in `ps`

Better options:

  • Let the tool prompt you: mysql -u root -p.
  • Use config files with restricted permissions (~/.my.cnf with mode 600), or environment files.
  • With HISTCONTROL=ignorespace or ignoreboth, start the command with a space to keep it out of history.

Remove an entry that’s already there:

history | grep -n Secret
history -d 1234          # delete entry 1234 from memory
history -w               # write the cleaned history to the file

history -c clears the whole in-memory history. Also check the file itself (~/.bash_history), and rotate the password. Treat a leaked credential as compromised.

History is not an audit log

Users can edit or clear their own history, and it only records interactive Bash. For real auditing on servers that need it, use auditd, sudo logging (/var/log/auth.log records every sudo command) or session recording tools. See sudo.

Root’s history and sudo -i

sudo -i and sudo su - give you root’s shell, so commands go to /root/.bash_history, not yours. Commands run as sudo cmd from your own shell go into your history, and into the auth log.

Quick reference

Task How
Search Ctrl+R
Previous command !! (or ↑)
With sudo sudo !!
Last argument !$ or Alt+.
By number !123
Replace and rerun ^old^new
Timestamps HISTTIMEFORMAT='%F %T '
Don’t record start with a space (ignorespace)
Delete entry history -d N; history -w

Related: alias and tmux.

  • Linux
  • Bash
  • Productivity