15%

Save 15% on All Hosting Services

Test your skills and get Discount on any hosting plan

Use code:

Skills
Get Started
09.10.2024

The `history` Command in Linux: A Complete Guide to Bash History

The `history` command in Linux is a built-in Bash shell utility that records, displays, and manages every command executed in a terminal session. It reads from and writes to `~/.bash_history`, a plain-text file in each user's home directory, enabling you to recall, search, re-execute, and audit commands across sessions without retyping them.

For system administrators and power users, Bash history is not merely a convenience feature — it is an operational audit trail, a debugging tool, and a productivity multiplier. Understanding its internals, configuration variables, and security implications separates casual users from engineers who extract maximum value from the command line.

How Bash History Works Internally

When you open a terminal session, Bash loads the contents of `~/.bash_history` into an in-memory list. As you execute commands, they are appended to this in-memory buffer. When the session closes normally (via `exit` or `logout`), the buffer is flushed back to `~/.bash_history` according to the rules defined by your environment variables.

This architecture has a critical implication: if your session terminates abnormally (power loss, SSH disconnect, `kill -9`), commands from that session may never be written to disk. This is a common source of confusion when administrators lose track of commands run during an interrupted session.

Two shell options modify this default write-on-exit behavior:

  • `shopt -s histappend` — appends new history to `~/.bash_history` instead of overwriting it. This is essential in multi-session environments.
  • `PROMPT_COMMAND='history -a'` — forces Bash to append the latest command to the history file after every prompt, enabling real-time persistence and cross-terminal visibility.

Without `histappend`, the last shell to close wins — it overwrites the history file, silently discarding entries from all other concurrent sessions.

Basic Usage of the `history` Command

Display the Full Command History

“`bash

history

“`

Outputs a numbered list of stored commands. The number on the left is the history index, used for event designators.

Display a Specific Number of Recent Commands

“`bash

history 20

“`

Shows the last 20 commands. Useful when you need a quick look at recent activity without scrolling through hundreds of entries.

Write Current Session History to File Immediately

“`bash

history -w

“`

Forces an immediate write of the in-memory history buffer to `~/.bash_history`. Use this before closing a critical session to ensure nothing is lost.

Read History from File into Current Session

“`bash

history -r

“`

Reloads `~/.bash_history` into the current session's memory. Useful when you want to access commands typed in another terminal window during the same login.

Recalling and Re-Executing Commands

Event Designators with `!`

Bash's event designator syntax allows direct re-execution of historical commands by reference:

DesignatorBehavior
`!!`Re-runs the immediately preceding command
`!n`Runs the command at history index `n`
`!-n`Runs the command `n` positions back from current
`!string`Runs the most recent command beginning with `string`
`!?string?`Runs the most recent command containing `string` anywhere
`!$`Substitutes the last argument of the previous command
`!*`Substitutes all arguments of the previous command

Practical example — reusing the last argument:

“`bash

mkdir /var/www/myproject

cd !$

“`

`!$` expands to `/var/www/myproject`, saving you from retyping the path. This is one of the most underused yet high-value features of Bash history.

Previewing before executing:

Append `:p` to any event designator to print the command without running it:

“`bash

!42:p

“`

This is a critical safety habit when working on production servers. Always preview destructive commands before execution.

Word Designators for Argument Extraction

Beyond re-running entire commands, Bash lets you extract specific arguments from history entries:

“`bash

!!:2 # Second word (argument) of the last command

!!:1-3 # Words 1 through 3 of the last command

!ssh:$ # Last argument of the most recent ssh command

“`

This level of granularity is invaluable when constructing complex pipelines or repeating operations on the same file paths.

Keyboard Shortcuts for History Navigation

ShortcutAction
`Up Arrow` / `Ctrl+P`Move to the previous command
`Down Arrow` / `Ctrl+N`Move to the next command
`Ctrl+R`Incremental reverse search through history
`Ctrl+S`Forward incremental search (requires `stty -ixon`)
`Alt+.`Insert the last argument of the previous command
`Ctrl+G`Cancel the current history search

Note on `Ctrl+S`: By default, `Ctrl+S` triggers XON/XOFF flow control and freezes the terminal. To enable forward history search, add `stty -ixon` to your `~/.bashrc`.

Reverse Search with `Ctrl+R`

“`

(reverse-i-search)`git': git commit -am "fix: resolve race condition"

“`

Type a substring and Bash incrementally matches the most recent command containing it. Press `Ctrl+R` again to cycle to older matches. Press `Enter` to execute, or `Ctrl+G` to abort without running anything.

For high-volume history searches, pipe through `grep`:

“`bash

history | grep "docker run"

history | grep -E "^[[:space:]]+[0-9]+[[:space:]]+ssh"

“`

Editing and Managing History Entries

Delete a Specific Entry

“`bash

history -d 87

“`

Removes the command at index 87 from the in-memory list. To make this permanent, follow with `history -w` to write the modified list back to disk.

Delete a Range of Entries

“`bash

for i in $(seq 85 90); do history -d 85; done

“`

Because deletion shifts indices, always delete the same index number in a loop rather than incrementing it.

Clear the Entire In-Memory History

“`bash

history -c

“`

Wipes the current session's history buffer. This does not touch `~/.bash_history` on disk.

Completely Purge All History

“`bash

history -c && history -w

“`

Clears the in-memory buffer and then writes the empty buffer to `~/.bash_history`, effectively truncating the file. This is the correct two-step sequence — using `> ~/.bash_history` alone does not clear the in-memory buffer, so the file may be repopulated on session exit.

Configuring Bash History: Environment Variables

All history behavior is governed by environment variables, typically set in `~/.bashrc` (interactive non-login shells) or `~/.bash_profile` / `~/.profile` (login shells). Changes take effect after sourcing the file:

“`bash

source ~/.bashrc

“`

`HISTSIZE`

Controls how many commands are held in memory during an active session.

“`bash

export HISTSIZE=10000

“`

Setting this to `0` disables in-memory history entirely. Setting it to `-1` (in Bash 4.3+) makes it unlimited.

`HISTFILESIZE`

Controls the maximum number of lines stored in `~/.bash_history` on disk.

“`bash

export HISTFILESIZE=20000

“`

When the file exceeds this limit, Bash trims the oldest entries. For compliance-sensitive environments, set this to a large value and pair it with log rotation.

`HISTCONTROL`

Determines filtering rules for which commands get recorded.

ValueBehavior
`ignoredups`Skips consecutive duplicate commands
`ignorespace`Skips commands prefixed with a space
`ignoreboth`Combines both of the above
`erasedups`Removes all previous occurrences of a command before adding the new one

“`bash

export HISTCONTROL=ignoreboth

“`

Security use-case for `ignorespace`: Prefix any command containing a password or secret with a space to prevent it from being recorded:

“`bash

mysql -u root -pSuperSecretPassword

“`

This is a widely used operational security practice on shared or multi-user systems.

`HISTTIMEFORMAT`

Adds a timestamp to each history entry, stored as a comment line in `~/.bash_history`.

“`bash

export HISTTIMEFORMAT="%Y-%m-%d %H:%M:%S "

“`

Output example:

“`

487 2024-11-14 09:32:17 systemctl restart nginx

488 2024-11-14 09:32:45 tail -f /var/log/nginx/error.log

“`

Timestamps are essential for post-incident forensics on VPS Hosting environments and dedicated infrastructure. Without them, you know *what* was run but not *when*.

`HISTIGNORE`

A colon-separated list of glob patterns. Commands matching any pattern are not saved to history.

“`bash

export HISTIGNORE="ls:ll:la:cd:pwd:exit:clear:history"

“`

This prevents trivial commands from polluting your history and diluting search results. You can also use wildcards:

“`bash

export HISTIGNORE="*password*:*secret*:*token*"

“`

This is a defense-in-depth measure — combine it with `ignorespace` for maximum credential hygiene.

Bash History Configuration Variables: Full Reference Table

VariableDefaultPurpose
`HISTSIZE`500–1000Commands held in memory per session
`HISTFILESIZE`500–2000Lines stored in `~/.bash_history`
`HISTCONTROL`(unset)Filtering rules for recorded commands
`HISTTIMEFORMAT`(unset)Timestamp format prepended to entries
`HISTIGNORE`(unset)Glob patterns for commands to exclude
`HISTFILE``~/.bash_history`Path to the history file
`histappend` (shopt)offAppend vs. overwrite on session exit

Sharing History Across Multiple Terminal Sessions

By default, each Bash session maintains its own isolated history buffer. Commands typed in Terminal A are invisible to Terminal B until both sessions close and the file is written. For administrators managing multiple SSH sessions simultaneously on Dedicated Servers, this creates gaps in the operational record.

The recommended configuration for real-time cross-session history sharing:

“`bash

~/.bashrc

export HISTSIZE=100000

export HISTFILESIZE=100000

export HISTCONTROL=ignoreboth

export HISTTIMEFORMAT="%Y-%m-%d %H:%M:%S "

shopt -s histappend

PROMPT_COMMAND='history -a; history -c; history -r'

“`

What this does:

  • `history -a` — appends the latest command to the file
  • `history -c` — clears the in-memory buffer
  • `history -r` — reloads the file into memory

After each command, every terminal session sees the complete, unified history from all active sessions. The trade-off is a slight overhead on `PROMPT_COMMAND` execution, which is negligible in practice.

Searching History Efficiently: Advanced Techniques

The `fzf` tool transforms history search from a linear scan into an interactive fuzzy-match interface:

“`bash

Install fzf (Debian/Ubuntu)

sudo apt install fzf

Bind Ctrl+R to fzf-powered history search

Add to ~/.bashrc:

[ -f ~/.fzf.bash ] && source ~/.fzf.bash

“`

Once configured, `Ctrl+R` opens a full-screen fuzzy search over your entire history. This is particularly powerful with large history files (10,000+ entries) where `grep` becomes cumbersome.

Extracting History for Scripting

“`bash

Export all unique commands containing "iptables" to a script

history | grep iptables | awk '{$1=""; print $0}' | sort -u > iptables_audit.sh

“`

This pattern is useful for reconstructing runbooks from ad-hoc commands executed during incident response.

Security Considerations for Bash History

Bash history is a double-edged tool. It accelerates legitimate workflows but also represents a significant attack surface.

Key risks and mitigations:

  • Credential exposure: Passwords passed as command-line arguments (e.g., `curl -u admin:password`) are stored in plaintext in `~/.bash_history`. Use `ignorespace`, `HISTIGNORE`, or environment variables instead.
  • Privilege escalation forensics: Attackers who gain shell access routinely read `~/.bash_history` to understand the environment, discover credentials, and identify high-value targets. Set restrictive permissions: `chmod 600 ~/.bash_history`.
  • History tampering: A compromised user can run `history -c && history -w` to erase all evidence. For auditing purposes on production systems, consider `auditd` or `syslog`-based command logging, which cannot be manipulated by the user.
  • Root history isolation: The root user's history is stored in `/root/.bash_history`. Ensure this file is not world-readable and is included in your backup and audit scope.

For environments requiring strict command auditing — such as PCI-DSS or SOC 2 compliant infrastructure — Bash history alone is insufficient. Pair it with kernel-level auditing via `auditd` and centralized log shipping.

Bash History vs. Alternative Shell History Systems

FeatureBash HistoryZsh HistoryFish History
Default history file`~/.bash_history``~/.zsh_history``~/.local/share/fish/fish_history`
Timestamp supportVia `HISTTIMEFORMAT`Built-inBuilt-in (YAML format)
Duplicate handling`HISTCONTROL``HIST_IGNORE_DUPS` optionAutomatic deduplication
Cross-session sharingManual (`PROMPT_COMMAND`)`INC_APPEND_HISTORY` optionAutomatic (shared by default)
Search interface`Ctrl+R` (linear)`Ctrl+R` (linear)Syntax-highlighted, context-aware
Max history size`HISTFILESIZE` variable`SAVEHIST` variableNo hard limit
Locking mechanismNone (race conditions possible)File locking supportedSQLite-backed (atomic writes)

Bash history's primary limitation is its lack of built-in locking, which can cause race conditions when multiple sessions write simultaneously. Zsh and Fish handle this more gracefully at the shell level.

Practical Configuration for Production Environments

The following is a battle-tested `~/.bashrc` history configuration suitable for production Linux servers, including those running VPS with cPanel or custom control panels:

“`bash

— Bash History Configuration —

export HISTSIZE=50000

export HISTFILESIZE=50000

export HISTCONTROL=ignoreboth:erasedups

export HISTTIMEFORMAT="%Y-%m-%d %H:%M:%S "

export HISTIGNORE="ls:ll:la:cd:pwd:exit:clear:bg:fg:jobs"

export HISTFILE=~/.bash_history

Append to history file; don't overwrite

shopt -s histappend

Save and reload history after each command

PROMPT_COMMAND='history -a; history -c; history -r'

Enable multi-line command history as single entry

shopt -s cmdhist

Store multi-line commands with embedded newlines

shopt -s lithist

“`

`cmdhist` and `lithist` deserve special mention. Without `cmdhist`, a multi-line command (like a `for` loop typed interactively) is stored as separate lines, making it impossible to re-execute cleanly. With `cmdhist` enabled and `lithist` set, the entire construct is stored as a single history entry with literal newlines, preserving its structure.

Automating History-Based Workflows

Generate a Command Frequency Report

“`bash

history | awk '{print $2}' | sort | uniq -c | sort -rn | head -20

“`

This reveals your 20 most-used commands — useful for identifying candidates for aliases or shell functions.

Audit `sudo` Usage

“`bash

history | grep sudo | awk '{$1=""; print $0}'

“`

On shared VPS Control Panels environments, this provides a quick audit of privileged operations performed during a session.

Reconstruct a Session Timeline

“`bash

HISTTIMEFORMAT="%Y-%m-%d %H:%M:%S " history | grep "2024-11-14"

“`

Filters all commands executed on a specific date — invaluable during post-incident reviews.

Key Technical Takeaways and Decision Checklist

Before deploying a Bash history configuration in any environment, validate the following:

  • `shopt -s histappend` is set — prevents history loss from concurrent sessions overwriting each other
  • `HISTSIZE` and `HISTFILESIZE` are both configured — setting only one leaves the other at its default, causing unexpected truncation
  • `HISTTIMEFORMAT` is enabled — without timestamps, history has no forensic value
  • `HISTCONTROL=ignoreboth` is set at minimum — reduces noise and prevents credential-adjacent commands from being logged
  • `HISTIGNORE` excludes trivial commands — keeps history signal-to-noise ratio high
  • `~/.bash_history` has `chmod 600` — prevents other users from reading your command history
  • `cmdhist` is enabled — ensures multi-line commands are stored as coherent units
  • `PROMPT_COMMAND` syncs history in real time — required for multi-session environments
  • `auditd` is deployed alongside — for production systems where tamper-proof logging is required
  • Credentials are never passed as CLI arguments — use environment variables, `.netrc`, or secrets managers instead

Frequently Asked Questions

Why does my Bash history disappear after closing an SSH session?

This typically happens because `shopt -s histappend` is not set. Without it, each session overwrites `~/.bash_history` on exit. If the session terminates abnormally (network drop, `kill -9`), the write never happens at all. Set `histappend` and `PROMPT_COMMAND='history -a'` to persist commands in real time.

How do I prevent passwords from being saved in Bash history?

Use two complementary techniques: prefix the command with a space (requires `HISTCONTROL=ignorespace` or `ignoreboth`), and add sensitive command patterns to `HISTIGNORE`. For long-term hygiene, never pass secrets as CLI arguments — use environment variables or dedicated secrets management tools.

What is the difference between `HISTSIZE` and `HISTFILESIZE`?

`HISTSIZE` controls how many commands Bash keeps in memory during an active session. `HISTFILESIZE` controls how many lines are retained in `~/.bash_history` on disk. Both must be set explicitly — a large `HISTSIZE` with a small `HISTFILESIZE` means your in-session history is rich, but most of it is discarded when the session ends.

Can deleted history entries be recovered?

Once `history -c && history -w` is executed, the in-memory buffer is cleared and the file is truncated — standard recovery is not possible. However, if your system uses filesystem snapshots or backup solutions, the previous version of `~/.bash_history` may be recoverable from a snapshot. This is another reason to implement `auditd` for tamper-proof logging on critical infrastructure.

How do I share Bash history across multiple simultaneous terminal sessions?

Add the following to `~/.bashrc`: `shopt -s histappend` and `PROMPT_COMMAND='history -a; history -c; history -r'`. This forces each session to append its latest command to the shared file and reload the full file after every prompt, giving all active terminals a unified, real-time view of command history.

15%

Save 15% on All Hosting Services

Test your skills and get Discount on any hosting plan

Use code:

Skills
Get Started