If you spend any time in a Linux shell, your command history becomes your second brain. Good history habits make you faster, more consistent, and less likely to “oops” your way into downtime.
In this guide you’ll learn practical ways to work with command history: listing it, searching it, re-running commands safely, and turning common workflows into reliable aliases.
Why command history matters (especially on servers)
- Speed: stop retyping long commands, flags, paths, and pipelines.
- Consistency: reuse the exact command that worked last time.
- Auditing your own work: quickly review what you ran during an incident.
- Safety: history tools can reduce mistakes—if you use them carefully.
1) The basics: the history command
Show your recent command history
history
You’ll typically see numbered entries. Those numbers are useful: you can re-run a specific entry by referencing its ID (we’ll get to that).
Show only the last N commands
history 20
Search history with grep (quick and reliable)
history | grep -i "nginx"
history | grep -E "systemctl|journalctl"
This is often the safest way to find a command: you can see context and confirm the full line before executing anything.
Where history is stored (and why it matters)
Most shells keep an in-memory history for the current session and also write it to a file when the session exits.
Common locations:
- Bash:
~/.bash_history - Zsh:
~/.zsh_history
If you open multiple terminals, you may notice history “missing” until sessions close. That’s normal behavior unless you tweak it.
Pro tip: timestamps for history entries (Bash)
If you want history to include timestamps (very handy during incident response), add this to ~/.bashrc:
export HISTTIMEFORMAT='%F %T '
Then reload:
source ~/.bashrc
2) Re-running commands with ! (history expansion)
The ! feature is powerful—and also the easiest way to shoot yourself in the foot if you’re not careful.
Use it when you understand exactly what will run.
Re-run the previous command
!!
Classic sysadmin workflow: you forgot sudo:
apt update
sudo !!
This re-runs the previous command prefixed with sudo. Great… unless the previous command was not the one you think it was. When in doubt: check your history first.
Run a specific history entry by number
history 10
!123
This runs entry 123. The key is to look at it before you run it.
Re-run the last command that starts with a prefix
!systemctl
!ssh
Example:
!journalctl
That will execute the most recent command whose line begins with journalctl.
Search-like expansions: run last command containing a word
!?nginx?
This executes the most recent command that contains “nginx” anywhere. Handy—but riskier than prefix matching because it’s easier to match something unexpected.
Quick substitutions: fix a typo without retyping
If you typed a command with one wrong word, you can substitute text in the previous command:
# Oops, wrong directory name:
cd /var/log/ngnix
# Fix "ngnix" to "nginx" in the previous command:
^ngnix^nginx^
This reruns the previous command with the first occurrence replaced. It’s a small trick, but it saves time constantly.
Safety trick: expand before executing
Many shells can show you what a history expansion will become before actually running it. If you’re unsure, an easy safety pattern is:
- Use
history | grep ...to view the full command first - Or copy/paste the exact command you want and edit it explicitly
On production systems, “explicit is better than clever” is usually the right vibe.
3) Ctrl+R: reverse search (your fastest history tool)
Ctrl+R starts an interactive reverse search through your history. You type a few characters, and the shell searches backward for a matching command.
How to use it (Bash / Zsh)
- Press
Ctrl+R - Start typing a fragment (e.g.
journalctl -u) - Press
Ctrl+Ragain to cycle older matches - Press
Enterto run, or→(Right Arrow) to edit before running - Press
Ctrl+Gto cancel
Practical examples
Example: find the last time you tailed a service log
# Press Ctrl+R then type:
journalctl -u
Example: find that long rsync command you used once and didn’t save anywhere
# Ctrl+R then type:
rsync --
Example: quickly re-run a kubectl command with edits
# Ctrl+R then type:
kubectl get
When the match appears, hit the Right Arrow to bring it onto your prompt, tweak namespace/context/flags, then run it. This is how you stay fast without being reckless.
4) Aliases: turn your best commands into reusable shortcuts
Aliases are basically custom command nicknames. Use them for:
- Common long commands (
journalctlfilters,lsoptions, Docker/Kubernetes shortcuts) - “Safe defaults” (e.g., interactive prompts for risky commands)
- Consistency across machines (especially in your homelab or fleet)
Create an alias (temporary, current session)
alias ll='ls -lah'
alias gs='git status -sb'
Make aliases persistent
Add them to the appropriate file:
- Bash:
~/.bashrc - Zsh:
~/.zshrc
Then reload:
source ~/.bashrc
# or
source ~/.zshrc
Practical sysadmin aliases (useful + sane)
Service logs, “I just need the important bits”
alias jctl='journalctl -xe'
alias jnginx='journalctl -u nginx --since "1 hour ago" --no-pager'
alias jssh='journalctl -u ssh --since today --no-pager'
Networking quick checks
alias ports='ss -tulpn'
alias myip='ip -br a'
Disk and system sanity checks
alias dfh='df -hT'
alias duh='du -h --max-depth=1'
alias psa='ps auxf'
Safety aliases (controversial, but useful on the right machines)
On admin workstations or your personal account, you can make destructive commands a little safer:
alias rm='rm -i'
alias cp='cp -i'
alias mv='mv -i'
On servers, be careful: scripts and automation may rely on non-interactive behavior. A safer pattern on servers is to keep aliases for your interactive shell only, not for system scripts.
When aliases are the wrong tool: use functions instead
Aliases don’t accept parameters in a structured way. For anything that needs arguments, use a shell function. Example: “tail logs of a systemd service with a default time range”.
# Put in ~/.bashrc or ~/.zshrc
jfu() {
local unit="${1:-nginx}"
local since="${2:-1 hour ago}"
journalctl -u "$unit" --since "$since" --no-pager
}
Usage:
jfu nginx "30 min ago"
jfu ssh "today"
5) History hygiene and security (don’t leak secrets)
Command history can accidentally store sensitive info: tokens, passwords, database connection strings, API keys. This isn’t theoretical—it happens all the time.
Bad: secrets directly in commands
# Avoid patterns like this:
curl -H "Authorization: Bearer SUPER_SECRET_TOKEN" https://api.example.com
Better patterns
- Use environment variables (and ideally load them from a protected file)
- Use tools that prompt for secrets
- Use password managers / vaults for automation
Example (environment variable):
export API_TOKEN="..." # still sensitive, but at least not copied into every command
curl -H "Authorization: Bearer $API_TOKEN" https://api.example.com
Prevent commands from being saved (simple trick)
In Bash, commands that start with a space can be ignored if you enable it:
export HISTCONTROL=ignorespace
Then you can run a sensitive one-off like this (note the leading space):
some_command_with_a_secret
This isn’t a magic shield, but it reduces accidental leakage.
Remove a specific line from history (Bash)
If you accidentally logged something sensitive, you can delete by entry number:
history | tail
history -d 123
Then consider also cleaning the history file if it’s already been written, and rotate/secure access appropriately.
6) Practical workflows (real-life examples)
Workflow A: “I ran the perfect command last week… what was it?”
Ctrl+Rand type a fragment you remember (a flag, a file name, a service)- Edit with Right Arrow before running
- If it’s a keeper, turn it into an alias or a function
Workflow B: “I forgot sudo”
sudo !!
Use it, but develop the habit of verifying your last command first if you’re on a production system.
Workflow C: “I need to repeat a safe command across multiple hosts”
Find the command in history, then copy the exact line into:
- a shell script with logging
- an Ansible task
- a runbook snippet
History is a great scratchpad. For repeatability, move it into a proper automation tool.
7) Quick reference cheat sheet
# History listing
history
history 20
history | grep -i keyword
# History expansion (use carefully!)
!! # previous command
sudo !! # previous command with sudo
!123 # run history entry 123
!ssh # last command starting with ssh
!?nginx? # last command containing nginx
^old^new^ # replace first occurrence in previous command
# Reverse search
Ctrl+R # reverse search
Ctrl+R again # older matches
Right Arrow # edit result
Ctrl+G # cancel
# Aliases
alias ll='ls -lah'
unalias ll
Conclusion
Command history is one of the highest ROI skills you can build in the shell. Use Ctrl+R as your primary “find it fast” tool, use history for visibility and verification, use ! expansions sparingly and intentionally, and convert your best repeated commands into aliases or functions. That combo makes you faster and safer—exactly what you want when you’re working on real servers.