Linux CLI: The Commands I Use Every Day
A collection of bash commands and shortcuts I reach for constantly, organized by what I'm trying to do rather than by category.

I've been maintaining a text file on my desktop called commands.txt for about four years now. Every time I look something up, solve a problem, or learn a new trick in the terminal, I paste the command into that file with a note about what it does. It's gotten long. A lot of it is repetitive or obsolete. But going through it recently, I pulled out the stuff I genuinely use on a regular basis and figured I'd organize it here.
This isn't a systematic introduction to the Linux command line. It's what I actually type, organized roughly by the kinds of problems I'm trying to solve when I open a terminal.
Moving Around Faster
I spent my first year in the terminal using arrow keys to move through commands and hitting backspace to fix typos. Then someone showed me the keyboard shortcuts and I felt stupid for not learning them sooner.
| Keystroke | What it does |
| :--- | :--- |
| Ctrl + R | Reverse search through command history. Start typing and it shows matching previous commands. |
| Ctrl + A | Jump to the beginning of the line. |
| Ctrl + E | Jump to the end of the line. |
| Alt + B | Move back one word. |
| Alt + F | Move forward one word. |
| Ctrl + U | Delete everything before the cursor. |
| Ctrl + K | Delete everything after the cursor. |
| Ctrl + W | Delete the previous word. |
| Ctrl + L | Clear the screen. Same as typing clear but faster. |
Ctrl + R is the one I'd miss the most if I had to give them up. I probably use it 40-50 times a day. Long docker commands, ssh commands with IP addresses I can't remember, complex find invocations I typed once three weeks ago โ instead of retyping them, I hit Ctrl + R and type the first few characters.
The word-jumping shortcuts (Alt + B and Alt + F) are the next most useful. When you've typed out a long command and need to change something in the middle, jumping word by word is way faster than holding the arrow key.
One thing that frustrated me early on โ these shortcuts might not work in every terminal emulator. macOS Terminal requires enabling "Use Option as Meta Key" for the Alt shortcuts to work. iTerm2 has a similar setting. I don't remember the specifics for Windows Terminal but I know it needed some configuration too.
When Something Is Wrong on a Server
There's a pattern to how I diagnose problems on a server. It usually goes: what's using resources โ what's listening on ports โ what does the log say. Here are the commands for each.
What processes are eating CPU or memory:
ps aux --sort=-%mem | head -15
This sorts all processes by memory usage (descending) and shows the top 15. Replace -%mem with -%cpu if CPU is the concern. I've caught runaway Node.js processes and memory-leaking Python scripts this way. The percentage columns aren't always accurate on containers, though โ they show percentages relative to the host, not the container limits. Something to watch out for.
What's listening on a port:
ss -tulnp | grep 3000
ss is the modern replacement for netstat. Shows which process is bound to port 3000. I use this constantly when I get "address already in use" errors โ usually it's a zombie process from a crashed dev server.
There's also lsof -i :3000 which gives you similar information in a different format. I switch between them depending on what's installed on the machine I'm working on.
Watching a command update in real time:
watch -n 2 'docker stats --no-stream'
watch re-runs the command every N seconds and shows the output. I use this when I'm trying to catch a resource spike in the act. The --no-stream flag on docker stats is necessary because without it, docker stats runs in its own loop and watch can't capture it properly.
Finding Files
find is powerful but the syntax is hostile. I still look up the flags every time.
Find files modified in the last 24 hours:
find . -mtime -1 -type f
Find and delete all Python cache files:
find . -name "*.pyc" -delete
Be careful with -delete. It doesn't ask for confirmation. I've typed this wrong before โ put -delete before -name and it tried to delete everything. The order of flags matters with find and getting them wrong can be destructive. I always do a dry run first without -delete to see what it would match.
Find the biggest directories:
du -sh /* 2>/dev/null | sort -rh | head -10
The 2>/dev/null suppresses permission-denied errors for directories you can't read. Without it, you get a screen full of error messages mixed in with the useful output.
I've mostly switched to fd for file finding (a Rust rewrite of find with better syntax) and ncdu for disk usage analysis (interactive, lets you drill into directories). Both are noticeably faster on large filesystems. But find and du are on every system, which matters when you're SSH'd into a minimal server that doesn't have your nice tools installed.
Text Processing
The pipe-things-together approach is one of those Unix ideas that sounds academic until you actually need it, and then it's surprisingly practical.
Pull all email addresses from a file:
grep -oP '[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}' dump.txt
The -o flag outputs only the matching part (not the whole line), and -P enables Perl-compatible regex. I've used variations of this to extract URLs, IP addresses, and phone numbers from log files.
See the top 10 IPs hitting your web server:
awk '{print $1}' /var/log/nginx/access.log | sort | uniq -c | sort -rn | head -10
This chain does: extract the first column (IP address) โ sort them โ count duplicates โ sort by count descending โ show top 10. When a server is being hammered and you need to quickly identify who's doing it, this is faster than opening any GUI tool.
Find and replace a string across all JavaScript files:
find . -name "*.js" -exec sed -i 's/oldFunction/newFunction/g' {} +
I'm honestly not very comfortable with sed for anything beyond simple substitutions. The in-place editing (-i) always makes me nervous. On macOS, sed -i requires an empty string argument (sed -i '' 's/...') which is different from GNU sed on Linux. That inconsistency has tripped me up more than once. For anything complex, I'll use a proper text editor or a find-and-replace tool like sd.
Shell Scripts That Don't Silently Fail
Every bash script I write starts with the same three flags. I don't even think about it anymore.
#!/bin/bash
set -euo pipefail
-eโ Exit immediately if any command exits with a non-zero status. Without this, your script happily continues after a failure and the resulting state is unpredictable.-uโ Treat undefined variables as errors. Without this, referencing a variable you forgot to set just evaluates to an empty string, which can cause bizarre behavior. I once ran a cleanup script that hadrm -rf "$DEPLOY_DIR/"where$DEPLOY_DIRwas unset. Without-u, that evaluates torm -rf /. Thankfully I caught it in testing. With-u, the script would have stopped and told me the variable was undefined.-o pipefailโ If any command in a pipe chain fails, the whole pipeline fails. Without this,curl url | process_datareturns the exit code ofprocess_dataeven ifcurlfailed. Your script thinks everything worked when the input data was actually empty.
These flags have saved me from subtle, hard-to-debug failures so many times that I consider them mandatory. Some people add set -x too, which prints each command before it executes โ useful for debugging but too noisy for regular use.
The Archive Extractor I Keep in My .bashrc
I got tired of looking up tar flags. tar xzf? tar xjf? tar xJf? I can never remember which flag goes with which compression format. So I wrote a function:
extract() {
case "$1" in
*.tar.bz2) tar xjf "$1" ;;
*.tar.gz) tar xzf "$1" ;;
*.tar.xz) tar xJf "$1" ;;
*.zip) unzip "$1" ;;
*.gz) gunzip "$1" ;;
*.tar) tar xf "$1" ;;
*.7z) 7z x "$1" ;;
*) echo "Don't know how to extract: $1" ;;
esac
}
Now I just type extract whatever.tar.gz and it figures out the right command. Small convenience but I use it often enough that it's earned its spot in my shell config.
Miscellaneous Things I Look Up Less Now
A few one-liners that I used to Google every time and eventually memorized:
Check which shell you're running:
echo $SHELL
Show your public IP:
curl -s ifconfig.me
Generate a random password:
openssl rand -base64 24
Kill a process by name:
pkill -f "node server.js"
The -f flag on pkill matches against the full command line, not just the process name. Without it, pkill node would kill ALL Node processes. With -f, you can target a specific one.
See your command history with timestamps:
HISTTIMEFORMAT="%F %T " history | tail -20
This requires HISTTIMEFORMAT to be set, which it isn't by default on most systems. I add it to my .bashrc so history always includes timestamps. It's helped me reconstruct timelines during incident investigations more than once โ "when exactly did I run that deploy command?"
I'm sure there are better tools for most of these tasks. I keep meaning to explore more Rust-based CLI replacements. ripgrep has already replaced grep for me, and fd has mostly replaced find. But for the rest of it, the old GNU tools are what I know, they're on every server, and they work. Maybe I'll get around to modernizing the rest of my workflow at some point. Or maybe I'll still be typing ps aux | grep in five years.
Written by
Anurag Sinha
Developer who writes about the stuff I actually use day-to-day. If I got something wrong, let me know.
Found this useful?
Share it with someone who might find it helpful too.
Comments
Loading comments...
Related Articles
My Terminal Setup, Explained
The Zsh, tmux, and Git configs I use daily. Annotated so you can understand what each block does and take what's useful.
Learning Docker โ What I Wish Someone Had Told Me Earlier
Why most Docker tutorials fail beginners, the critical difference between images and containers, and what actually happens when you run a container.
Cloud-Native Architecture: What It Means and What It Costs
Reference definitions for the 12-factor app methodology, containerization, infrastructure as code, and CI/CD pipelines.