bash system
you type in it 8 hours a day and you know nothing about it.
The bash man page is 6,400 lines long. It is, by a wide margin, the longest man page on most Linux systems. You have been typing commands into bash every single day for years and you have never read one line of it.
You open a terminal. You type ls. You type cd. You type grep. You press the up arrow to find a command you ran earlier. Sometimes you press it forty-seven times because you can’t remember how long ago you ran it. You retype commands that are twelve words long because you don’t know there’s a faster way. You redirect output with > because someone showed you once in 2016 and you’ve been cargo-culting it ever since.
bash is the most powerful interactive tool on your computer. You’ve been using it as a typewriter.
Unless you’re running Windows then wtf none of this applies to you. But hey, come to the dark side, go install WSL2 and you can follow along. We’ll wait. Impatiently.
If you’re lazy like me (all sysadmins are!) then click here for the bash cheat sheet.
History: stop pressing the up arrow 47 times
You ran a command twenty minutes ago. You need it again. So you press Up. And Up. And Up. And Up. You scroll through your entire afternoon of work one command at a time, squinting at each one, hoping you recognize it before your finger cramps.
Search your history
Ctrl+r
Press Ctrl+r and start typing any part of the command. bash searches backward through your history and shows you the match instantly. Press Ctrl+r again to cycle through older matches. Press Enter to run it. Press Ctrl+c to cancel.
This is the single most useful thing on this page. If you learn nothing else, learn this.
Show your full history
history
Every command you’ve run, numbered. Probably thousands of them. Pipe it through grep to find what you need:
history | grep rsync
Every rsync command you’ve ever run. With numbers.
Run a command from history by number
!423
Runs command number 423 from your history. Combine with history | grep and you can find and re-run anything in seconds.
Run the last command again
!!
Two bangs. Runs whatever you just ran. Most useful when you forgot sudo:
sudo !!
Runs the previous command with sudo prepended. You’ll use this at least once a week.
Run the last command that started with something
!rsync
Runs the most recent command that started with “rsync.” Dangerous if you don’t remember exactly what that command was. Use !rsync:p to print it first without running it.
Keyboard shortcuts: stop reaching for the mouse
Your terminal doesn’t have a cursor you can click. Stop trying. bash has keyboard shortcuts for navigating and editing the command line that are faster than any mouse.
Moving around
| Shortcut | What it does |
|---|---|
Ctrl+a |
Jump to the beginning of the line. |
Ctrl+e |
Jump to the end of the line. |
Alt+f |
Jump forward one word. |
Alt+b |
Jump backward one word. |
You typed a 90-character command and the typo is at the beginning. You were going to press the left arrow 87 times. Now press Ctrl+a once.
Deleting text
| Shortcut | What it does |
|---|---|
Ctrl+w |
Delete the word before the cursor. |
Ctrl+k |
Delete from cursor to end of line. |
Ctrl+u |
Delete from cursor to beginning of line. |
Alt+d |
Delete the word after the cursor. |
Ctrl+u is what you press when you’ve typed a password wrong and need to start over. You’ve been holding backspace for four seconds like an animal.
The undo button nobody knows about
Ctrl+_
Yes, bash has undo. You deleted half your command with Ctrl+k by accident? Ctrl+_ brings it back. This has been here the entire time.
Tab completion: let bash type for you
You know about tab completion. You press Tab and bash finishes the filename. But you’re only using the surface.
Double-tab to see options
cd /etc/sys[Tab][Tab]
If there are multiple matches, double-tap Tab and bash lists all of them. Pick the one you want and keep typing.
It works on commands too
system[Tab][Tab]
Shows every command that starts with “system” — systemctl, systemd-analyze, systemd-cat, etc. You’ve been typing full command names this entire time.
And on options (in newer bash)
git ch[Tab][Tab]
Shows checkout, cherry, cherry-pick. Some commands have programmable completion that knows their subcommands and flags. If it doesn’t work, the package bash-completion makes it work:
sudo apt install bash-completion
sudo dnf install bash-completion
Brace expansion: the laziest way to type less
This is the feature that will make you feel like you’re cheating.
Create multiple files at once
touch report_{jan,feb,mar,apr}.txt
Creates report_jan.txt, report_feb.txt, report_mar.txt, report_apr.txt. One command. Four files.
Create a sequence
mkdir day_{01..31}
Creates day_01 through day_31. Thirty-one directories. One command. Zero carpal tunnel.
Rename with brace expansion
mv config.yml{,.bak}
That expands to mv config.yml config.yml.bak. You just made a backup in six fewer characters. This trick alone justifies reading this page.
Create directory trees
mkdir -p project/{src,tests,docs}/{v1,v2}
Creates project/src/v1, project/src/v2, project/tests/v1, project/tests/v2, project/docs/v1, project/docs/v2. Six directories. One command. Your file manager just got jealous.
Command substitution: use output as input
Embed the output of one command inside another.
echo "Today is $(date +%Y-%m-%d)"
Today is 2026-03-15
The $() runs the command inside it and substitutes the output inline. Use it anywhere:
tar -czf backup_$(date +%Y%m%d).tar.gz /home/owner/projects/
Creates backup_20260315.tar.gz. The filename includes today’s date automatically. Your backup script is one line.
kill $(pgrep -f "node server")
Finds the PID of “node server” and kills it in one shot. No ps aux | grep, no copy-pasting PIDs.
Job control: run things in the background
You started a long-running command and now your terminal is stuck. You can’t type anything else. You’re trapped. So you open a new terminal like everyone else.
Or you could use job control.
Run something in the background
./build.sh &
The & at the end runs it in the background. Your terminal is free immediately. The process keeps running.
Suspend a running process
Already running something and wish you’d backgrounded it?
Ctrl+z
Suspends the process. You get your prompt back. The process is paused, not dead.
Resume in the background
bg
The suspended process continues running in the background.
Resume in the foreground
fg
Brings it back to the foreground. Like it never left.
List background jobs
jobs
Shows everything you’ve backgrounded or suspended. Numbered. Use fg %2 to bring back job number 2.
Redirects and pipes: plumbing 101
You’ve been using > and | without understanding what they are. Let’s fix that.
Output to a file (overwrite)
echo "hello" > file.txt
Creates or overwrites file.txt with “hello.”
Output to a file (append)
echo "another line" >> file.txt
Adds to the end of file.txt. Doesn’t destroy what’s already there. The extra > is the difference between “add to the log” and “destroy the log.”
Redirect errors separately
./script.sh > output.txt 2> errors.txt
Standard output goes to output.txt. Errors go to errors.txt. 2> redirects file descriptor 2 (stderr). If you don’t do this, errors and output get mixed together and good luck figuring out which is which.
Throw away errors
find / -name "*.conf" 2>/dev/null
Sends errors to /dev/null — the void. Gone. No “Permission denied” spam flooding your screen. You’ve seen this in other people’s commands and never knew what it meant. Now you do.
Pipe output to another command
cat access.log | sort | uniq -c | sort -rn | head -10
Each | sends the output of one command as input to the next. This is the Unix philosophy: small tools that do one thing, chained together to do big things. Your GUI can’t do this because GUI tools don’t talk to each other.
The flags that actually matter
These aren’t flags for bash itself — they’re built-in features you should know exist.
| Feature | What it does |
|---|---|
Ctrl+r |
Reverse search through command history. |
!! |
Repeat last command. sudo !! is the killer use case. |
!$ |
Last argument of previous command. mkdir foo && cd !$ |
Tab |
Complete filenames, commands, and arguments. |
{} |
Brace expansion. {a,b,c} or {1..10}. |
$() |
Command substitution. Embed output inline. |
& |
Run command in background. |
Ctrl+z |
Suspend foreground process. |
> / >> |
Redirect output (overwrite / append). |
2> |
Redirect stderr. 2>/dev/null to discard. |
| |
Pipe output to next command. |
“But I use—”
Go on. Tell me.
“I use zsh.” zsh is great. It also supports 90% of everything on this page because zsh is mostly bash-compatible. The history search, the keyboard shortcuts, the redirects, the pipes — all the same. You switched shells for the fancy prompt and the auto-suggestions, which is fine, but the fundamentals didn’t change.
“I use fish.” Fish deliberately broke POSIX compatibility to be “friendlier.” Which means half the scripts on the internet won’t work and you’ll spend more time translating bash syntax to fish syntax than you saved with auto-suggestions. But your prompt has a cool fish emoji, so there’s that.
“PowerShell is more powerful.” PowerShell passes objects instead of text. That sounds impressive until you realize the entire Unix ecosystem — decades of tools, pipes, filters, and utilities — is built on text. grep, awk, sed, sort, uniq, cut, tr, jq — they all work on text. PowerShell replaced a universal interface with a proprietary one and called it progress.
“I use Oh My Zsh.” You installed a 1,400-file framework to get a fancy prompt and Git status in your terminal. You could have configured PS1 in three lines. But the framework also loaded 47 plugins you’ve never used, added 200ms to your shell startup time, and occasionally breaks after updates. You’re running a shell management framework. To manage your shell. Think about that.
“I don’t need to know this, I just type commands.” And every time you press Up forty times to find a command, retype a long path because you don’t know about tab completion, or open a new terminal because you don’t know about Ctrl+z — you’re wasting time. Small amounts. But every day. For years. It adds up. Learn the tool you already use.
bash cheat sheet
You made it. Or you skipped straight here. Either way, no judgment. Copy and paste these. Pin them. Tattoo them on your forearm. Whatever works.
History:
| What you’re doing | Command |
|---|---|
| Search history | Ctrl+r then type |
| Show all history | history |
| Search history for a command | history | grep pattern |
| Run by number | !423 |
| Repeat last command | !! |
| Repeat with sudo | sudo !! |
| Last argument of previous command | !$ |
Keyboard shortcuts:
| What you’re doing | Keys |
|---|---|
| Jump to start of line | Ctrl+a |
| Jump to end of line | Ctrl+e |
| Forward one word | Alt+f |
| Back one word | Alt+b |
| Delete word before cursor | Ctrl+w |
| Delete to end of line | Ctrl+k |
| Delete to start of line | Ctrl+u |
| Undo | Ctrl+_ |
Expansion and substitution:
| What you’re doing | Command |
|---|---|
| Create multiple files | touch file_{a,b,c}.txt |
| Numbered sequence | mkdir day_{01..31} |
| Quick backup | cp config.yml{,.bak} |
| Embed command output | echo "Date: $(date)" |
Job control:
| What you’re doing | Command |
|---|---|
| Run in background | command & |
| Suspend foreground process | Ctrl+z |
| Resume in background | bg |
| Resume in foreground | fg |
| List jobs | jobs |
The one shortcut:
Ctrl+r— reverse history search. If you remember nothing else from this page, remember this. It replaces pressing Up forty-seven times.