Article

Essential Linux Commands: The Toolkit Every Engineer Uses Daily

17 min read
Originally published on Medium. Read, clap or comment on Medium ↗

Knowing Linux commands is not about memorising a reference manual. It’s about having a core toolkit so deeply internalised that reaching for the right tool is instinctive — the way a chef reaches for a knife without looking.

Linux Zero to Hero · Episode 03 ·

Episodes 01 and 02 built the mental model — the Unix philosophy, the filesystem, inodes, permissions. Now we build the muscle memory.

This episode is different from a typical “Linux commands cheat sheet.” We’re not going to list 100 commands with one-line descriptions. We’re going to take the 30 commands you’ll actually reach for every day, understand them deeply, and see them in the real-world workflows where they earn their keep.

The goal: by the end of this episode, the commands below should feel like extensions of your hands. Not something you look up. Something you just use.

Navigation and Orientation

These are the commands that answer the first question you ask on any Linux system: where am I and what’s here?

# Where am I?
pwd
# /home/ubuntu/projects
# What's in the current directory?
ls # simple list
ls -l # long format (permissions, size, date)
ls -la # include hidden files (dotfiles)
ls -lh # human-readable sizes (KB, MB, GB)
ls -lt # sort by modification time, newest first
ls -lS # sort by size, largest first
ls -R # recursive listing
# Move around
cd /var/log # absolute path
cd logs/ # relative path
cd .. # up one level
cd ../.. # up two levels
cd ~ # home directory
cd - # previous directory (toggle between two locations)
# Show directory tree
tree /etc/nginx # requires: sudo apt install tree
tree -L 2 /etc/ # limit depth to 2 levels
tree -d /usr/ # directories only

The cd - trick is underused and enormously practical. When you're jumping between two directories repeatedly — say, /etc/nginx and /var/log/nginx — cd - toggles between them instantly.

Viewing File Contents

Reading files is something you do hundreds of times a day. Know every tool for it.

# Read the whole file
cat /etc/hosts # print entire file to terminal
cat -n /etc/hosts # with line numbers
cat -A /etc/hosts # show special characters (tabs, line endings)
# Read large files — page by page
less /var/log/syslog # most powerful pager
# Inside less:
# Space / f → next page
# b → previous page
# /pattern → search forward
# ?pattern → search backward
# n → next match
# G → end of file
# g → start of file
# q → quit
# Read the beginning or end of a file
head /var/log/syslog # first 10 lines
head -n 50 /var/log/syslog # first 50 lines
tail /var/log/syslog # last 10 lines
tail -n 100 /var/log/syslog # last 100 lines
tail -f /var/log/syslog # follow — watch new lines as they're added (Ctrl+C to stop)
tail -F /var/log/syslog # follow even if file is rotated (survives log rotation)
# Watch a file update in real time (more flexible than tail -f)
watch -n 2 cat /proc/loadavg # refresh every 2 seconds
# Count lines, words, characters
wc -l /etc/passwd # number of lines
wc -w /etc/hosts # number of words
wc -c /etc/hosts # number of bytes
wc /etc/hosts # lines, words, bytes together
# Fast file viewing for very large files
less +F /var/log/syslog # open less and immediately start following
💡 tail -f is one of the most-used commands in production. When you deploy an application and want to watch its log output in real time, tail -f /var/log/myapp/app.log is the first thing you run. tail -F is usually better — it handles log rotation gracefully.

Creating, Copying, Moving, and Deleting

# Create files
touch newfile.txt # create empty file, or update timestamp if it exists
touch -t 202401150900 file # set specific timestamp
echo "content" > file.txt # create with content (overwrites)
echo "more" >> file.txt # append to file
tee file.txt # write stdin to file AND display on screen
# Create directories
mkdir mydir
mkdir -p path/to/deep/dir # create intermediate directories as needed
mkdir -m 755 mydir # create with specific permissions
# Copy files and directories
cp file.txt backup.txt # copy file
cp -r /etc/nginx/ /tmp/nginx-backup/ # copy directory recursively
cp -p file.txt backup.txt # preserve permissions, timestamps, ownership
cp -u source dest # copy only if source is newer
cp -v file.txt backup.txt # verbose — show what's being copied
cp -i file.txt existing.txt # prompt before overwrite
# Move and rename
mv old.txt new.txt # rename
mv file.txt /tmp/ # move to directory
mv /tmp/file.txt . # move to current directory
mv -i source dest # prompt before overwrite
mv -v old new # verbose
# Delete files and directories
rm file.txt # delete file
rm -i file.txt # prompt before deleting
rm -f file.txt # force — no prompt, no error if not found
rm -r directory/ # delete directory recursively
rm -rf directory/ # force recursive delete (careful!)
rmdir emptydir/ # delete empty directory only
# Create and view file content quickly
cat > quicknote.txt << 'EOF'
Line one
Line two
Line three
EOF
cat quicknote.txt
⚠️ rm -rf has no undo. There is no trash bin in Linux. Deleted files do not go to a recoverable location. The infamous rm -rf / would delete everything on the system. Always double-check the path before running any rm -r command. In production, consider trash-cli as a safer alternative, or use mv to a staging directory before final deletion.

Searching Inside Files: grep

grep is the command you'll use more than almost any other. Learn it thoroughly.

# Basic search
grep "error" /var/log/syslog # lines containing "error"
grep -i "error" /var/log/syslog # case insensitive
grep -v "error" /var/log/syslog # invert — lines NOT containing "error"
grep -n "error" /var/log/syslog # show line numbers
grep -c "error" /var/log/syslog # count matching lines only
# Search multiple files
grep "error" /var/log/*.log # all log files
grep -r "password" /etc/ # recursive search through directory
grep -rl "password" /etc/ # recursive — show filenames only, not content
grep -l "nginx" /etc/ # list files that contain the pattern
# Context around matches
grep -A 3 "error" app.log # 3 lines After the match
grep -B 3 "error" app.log # 3 lines Before the match
grep -C 3 "error" app.log # 3 lines Context (before and after)
# Pattern matching
grep "^root" /etc/passwd # lines starting with "root" (^ = start)
grep "bash$" /etc/passwd # lines ending with "bash" ($ = end)
grep "^$" file.txt # empty lines
grep -E "error|warning|critical" /var/log/syslog # extended regex (OR)
grep -E "[0-9]{3}" file.txt # exactly 3 digits
grep -w "fail" /var/log/auth.log # whole word match only (not "failure")
grep -P "\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}" file # Perl regex — match IPs
# Practical grep workflows
# Find who's logged in from a specific IP
grep "192.168.1.100" /var/log/auth.log
# Find all failed SSH attempts
grep "Failed password" /var/log/auth.log | wc -l
# Find configuration lines, excluding comments
grep -v "^#" /etc/nginx/nginx.conf | grep -v "^$"
# Find error lines in all logs from today
grep "$(date '+%b %d')" /var/log/syslog | grep -i "error"

Text Manipulation

# Sort lines
sort file.txt # alphabetical
sort -r file.txt # reverse
sort -n numbers.txt # numeric sort
sort -rn numbers.txt # reverse numeric
sort -k2 file.txt # sort by second field
sort -t: -k3 -n /etc/passwd # sort passwd by UID (field 3, colon delimiter)
sort -u file.txt # sort and remove duplicates
# Remove duplicates
uniq sorted.txt # remove adjacent duplicates (input must be sorted)
uniq -c sorted.txt # count occurrences
uniq -d sorted.txt # show only duplicate lines
sort file.txt | uniq -c | sort -rn # count and rank occurrences
# Cut columns from text
cut -d: -f1 /etc/passwd # extract field 1, delimiter ':' (usernames)
cut -d: -f1,3 /etc/passwd # extract fields 1 and 3
cut -c1-10 file.txt # extract characters 1 through 10
# Translate or delete characters
tr 'a-z' 'A-Z' < file.txt # convert to uppercase
tr -d '\r' < windows.txt # delete carriage returns (fix Windows line endings)
tr -s ' ' < file.txt # squeeze multiple spaces into one
# Reverse lines
tac file.txt # reverse line order (cat backwards)
# Combine files
cat file1.txt file2.txt > combined.txt # concatenate
paste file1.txt file2.txt # side by side columns
paste -d, file1.txt file2.txt # comma-separated columns
# Compare files
diff file1.txt file2.txt # show differences
diff -u file1.txt file2.txt # unified format (used in git diffs)
diff -r dir1/ dir2/ # compare directories
comm file1.txt file2.txt # lines common to both (must be sorted)

Archiving and Compression

# tar — the standard archiving tool
# Create archive
tar -czf backup.tar.gz /etc/nginx/ # compress with gzip
tar -cjf backup.tar.bz2 /etc/nginx/ # compress with bzip2 (smaller, slower)
tar -cJf backup.tar.xz /etc/nginx/ # compress with xz (smallest, slowest)
tar -cf backup.tar /etc/nginx/ # no compression
# Extract archive
tar -xzf backup.tar.gz # extract gzip archive
tar -xzf backup.tar.gz -C /tmp/ # extract to specific directory
tar -xzf backup.tar.gz file.conf # extract specific file only
# List archive contents
tar -tzf backup.tar.gz # list without extracting
# Append to archive
tar -rzf backup.tar.gz newfile.txt
# tar flags decoded:
# c = create, x = extract, t = list
# z = gzip, j = bzip2, J = xz
# f = filename follows, v = verbose
# Quick compression of single files
gzip file.txt # creates file.txt.gz, removes original
gzip -k file.txt # keep original
gunzip file.txt.gz # decompress
bzip2 file.txt         # creates file.txt.bz2
bunzip2 file.txt.bz2
# zip (Windows-compatible)
zip archive.zip file1.txt file2.txt
zip -r archive.zip directory/
unzip archive.zip
unzip -l archive.zip # list contents without extracting

Working With Processes From the Command Line

# Run commands in background
long-running-command & # & sends to background immediately
nohup long-running-command & # run even after terminal closes
# Job control
jobs # list background jobs
fg # bring last background job to foreground
fg %2 # bring job 2 to foreground
bg # resume suspended job in background
Ctrl+Z # suspend foreground job
Ctrl+C # terminate foreground job
# Quick process info
ps aux | grep nginx # find nginx processes
pgrep nginx # just the PID(s) — cleaner
pidof nginx # same, slightly different
kill $(pgrep nginx) # kill by name
# Process substitution (use command output as a file)
diff <(ls dir1/) <(ls dir2/) # compare directory listings
wc -l <(find / -name "*.log") # count results of find

The Power of Pipes and Command Chaining

# Pipe (|): stdout of left → stdin of right
cat /etc/passwd | grep bash # find bash users
ps aux | grep -v grep | grep nginx # find nginx processes
# Sequential execution
mkdir /tmp/test && cd /tmp/test # && : run second only if first succeeds
rm file.txt || echo "file not found" # || : run second only if first fails
rm file.txt ; echo "done" # ; : run second regardless
# Command substitution: use output of a command as argument
echo "Current date: $(date)"
echo "Number of files: $(ls | wc -l)"
mkdir "backup-$(date +%Y%m%d)" # create dated directory
chown $(whoami) file.txt
# xargs: turn stdin lines into command arguments
find /tmp -name "*.tmp" | xargs rm # delete found files
find /etc -name "*.conf" | xargs ls -lh # list found files
echo "file1 file2 file3" | xargs touch # create multiple files
find . -name "*.py" | xargs grep "import os" # search inside found files
# xargs with multiple arguments
cat servers.txt | xargs -I{} ssh {} "uptime" # run command on each server

Keyboard Shortcuts That Change Everything

This section has an outsized impact on productivity. Most engineers never learn these — and they spend the rest of their Linux careers moving the cursor character by character.

Line navigation:
Ctrl+A → jump to start of line
Ctrl+E → jump to end of line
Alt+B → move back one word
Alt+F → move forward one word
Ctrl+W → delete word to the left
Alt+D → delete word to the right
Ctrl+K → delete from cursor to end of line
Ctrl+U → delete from cursor to start of line
Ctrl+Y → paste what was deleted (yank)
History:
Ctrl+R → reverse search through command history
(start typing, it finds the most recent match)
Ctrl+P → previous command (same as ↑)
Ctrl+N → next command (same as ↓)
!! → repeat last command
!n → repeat command number n from history
!string → repeat last command starting with "string"
!$ → last argument of previous command
Control:
Ctrl+C → interrupt/kill current process
Ctrl+Z → suspend current process
Ctrl+D → end of input (exit shell if at prompt)
Ctrl+L → clear screen (same as clear command)
Ctrl+S → pause output
Ctrl+Q → resume output
Tab completion:
Tab → complete filename/command
Tab Tab → show all completions when ambiguous

The single most important shortcut is Ctrl+R. Press it, start typing part of any command you’ve run before, and it finds the most recent match. Hit Ctrl+R again to cycle through older matches. This alone eliminates 80% of retyping.

# Try it:
# Press Ctrl+R, type "find /var", see your last find command in /var appear
# Press Ctrl+R again to see an older one
# Press Enter to run it, or Ctrl+C to cancel

History: Your Command Memory

# Show command history
history # all history
history 20 # last 20 commands
history | grep "apt install" # search history
# History expansion
!! # repeat last command
sudo !! # re-run last command with sudo (incredibly useful)
!grep # run last grep command
!?nginx # run last command containing "nginx"
!$ # last argument of previous command
!^ # first argument of previous command
# Clear history
history -c # clear session history
history -d 42 # delete entry 42
# History configuration in ~/.bashrc
export HISTSIZE=10000 # keep 10000 commands in memory
export HISTFILESIZE=20000 # keep 20000 in the history file
export HISTCONTROL=ignoredups # don't save duplicate consecutive commands
export HISTTIMEFORMAT="%F %T " # add timestamps to history

sudo !! deserves special mention. You run a command, get "Permission denied," and instead of re-typing the whole thing, you just type sudo !!. It reruns the last command with sudo prepended. It feels like cheating but it's completely legitimate.

Environment and Shell Variables

# See all environment variables
env
printenv
printenv PATH # specific variable
# Set and use variables
NAME="ubuntu"
echo $NAME # use the variable
echo ${NAME} # explicit boundary (use when concatenating)
echo "${NAME}_server" # correct: ubuntu_server
echo "$NAME_server" # wrong: treats NAME_server as the variable (empty)
# Export to child processes
export DATABASE_URL="postgres://localhost:5432/mydb"
# Now any program launched from this shell can read DATABASE_URL
# Unset a variable
unset NAME
# Special variables
echo $0 # current script/shell name
echo $? # exit code of last command (0 = success)
echo $$ # PID of current shell
echo $! # PID of last background command
echo $# # number of arguments (in a script)
echo $@ # all arguments (in a script)
echo $HOME # home directory
echo $PATH # executable search path
echo $USER # current username
echo $HOSTNAME # machine hostname
echo $SHELL # current shell
echo $EDITOR # preferred text editor
# PATH — where the shell looks for executables
echo $PATH
# /usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
# Add a directory to PATH
export PATH=$PATH:/opt/myapp/bin
# Now executables in /opt/myapp/bin are available as commands
# Make it permanent (add to ~/.bashrc or ~/.profile)
echo 'export PATH=$PATH:/opt/myapp/bin' >> ~/.bashrc
source ~/.bashrc # reload the file

Aliases: Building Your Own Command Vocabulary

# Create an alias for the current session
alias ll='ls -lah'
alias grep='grep --color=auto'
alias ..='cd ..'
alias ...='cd ../..'
alias mkdir='mkdir -pv' # always make parents, always verbose
alias cp='cp -i' # always prompt before overwrite
alias rm='rm -i' # always prompt before delete (safety)
# Use the alias
ll /etc/nginx
# Remove an alias
unalias ll
# See all defined aliases
alias
# Make aliases permanent — add to ~/.bashrc
cat >> ~/.bashrc << 'EOF'
# My custom aliases
alias ll='ls -lah --color=auto'
alias la='ls -A'
alias grep='grep --color=auto'
alias ..='cd ..'
alias ...='cd ../..'
alias h='history'
alias c='clear'
alias df='df -h'
alias du='du -h'
alias free='free -h'
alias ports='ss -tulnp'
alias myip='curl -s ifconfig.me'
alias update='sudo apt update && sudo apt upgrade -y'
EOF
source ~/.bashrc

Redirection Patterns You’ll Use Constantly

# Write stdout to file
command > file.txt # overwrite
command >> file.txt # append
# Write stderr to file
command 2> errors.txt
# Write both stdout and stderr
command > output.txt 2>&1 # classic syntax
command &> output.txt # modern bash syntax (same result)
# Discard output
command > /dev/null # discard stdout
command 2> /dev/null # discard stderr
command &> /dev/null # discard everything
# Read from file as stdin
command < input.txt
sort < unsorted.txt > sorted.txt
# Here document — multi-line input
cat > config.conf << 'EOF'
server_name = myapp
port = 8080
debug = false
EOF
# Here string — single line input
grep "pattern" <<< "this is the string to search"
base64 <<< "encode this string"
# tee — write to file AND stdout simultaneously
command | tee output.txt # write and display
command | tee -a output.txt # append and display
command | tee file1 file2 file3 # write to multiple files simultaneously
make build 2>&1 | tee build.log # save build output while watching it

Real-World Workflows

The true value of commands is how they combine. Here are the workflows you’ll actually run in production.

Investigating a server that’s behaving strangely

# What's running and consuming resources?
top # interactive
ps aux --sort=-%cpu | head # top CPU consumers
ps aux --sort=-%mem | head # top memory consumers
# How long has it been up? What's the load?
uptime
# Any recent errors?
tail -100 /var/log/syslog | grep -i error
journalctl -p err -n 50 # systemd-based systems
# Disk space emergency?
df -h
du -ah / --max-depth=3 2>/dev/null | sort -rh | head -20

Finding what changed recently

# Files modified in the last 24 hours
find /etc -mtime -1 -type f 2>/dev/null
# Files modified in the last 10 minutes
find /var/log -mmin -10 -type f
# Show recent auth events
tail -50 /var/log/auth.log
# Recent package installs
grep "install " /var/log/dpkg.log | tail -20

Processing a log file

# Count requests per HTTP status code
awk '{print $9}' /var/log/nginx/access.log | sort | uniq -c | sort -rn
# Top 10 IP addresses hitting the server
awk '{print $1}' /var/log/nginx/access.log | sort | uniq -c | sort -rn | head -10
# Requests per hour
awk '{print $4}' /var/log/nginx/access.log | cut -c2-15 | sort | uniq -c
# Find all 500 errors with their URLs
grep " 500 " /var/log/nginx/access.log | awk '{print $7}' | sort | uniq -c | sort -rn

Bulk file operations

# Rename all .txt files to .bak
for f in *.txt; do mv "$f" "${f%.txt}.bak"; done
# Find and replace text in multiple files
find . -name "*.conf" -exec sed -i 's/old-hostname/new-hostname/g' {} \;
# Create dated backups of config files
for f in /etc/nginx/*.conf; do cp "$f" "${f}.backup-$(date +%Y%m%d)"; done
# Delete files older than 30 days
find /var/log -name "*.log.gz" -mtime +30 -delete

Hands-On Lab

Work through each of these — type them, don’t paste:

# 1. Navigation mastery
cd /var/log && ls -lth | head -10 # most recently modified logs
cd /etc && ls -la | grep "^l" # symlinks in /etc
cd - # back to /var/log
cd - # back to /etc
# 2. File viewing
sudo tail -f /var/log/syslog & # follow syslog in background
echo "test message" | logger # send a message to syslog
# You should see the message appear in the tail output
kill %1 # stop the background job
# 3. grep workflows
grep -n "ubuntu" /etc/passwd # find your user entry
grep -c "" /etc/passwd # count users (lines)
grep -E "^[^:]*:[^:]*:0:" /etc/passwd # find UID 0 entries (root equivalents)
# 4. Pipelines
cat /etc/passwd | cut -d: -f1 | sort # alphabetical user list
ls /usr/bin | grep "^py" | wc -l # how many python commands?
cat /proc/meminfo | grep -E "MemTotal|MemFree|MemAvailable"
# 5. Keyboard shortcuts practice
# Type a long command, then use Ctrl+A, Ctrl+E, Alt+B, Alt+F
# Try Ctrl+R and search for a recent command
# Use sudo !! after a permission denied error
# 6. History
history | tail -20
history | grep "grep"
# Try !grep to re-run your last grep command
# 7. Variables and PATH
echo "My home is: $HOME"
echo "My shell is: $SHELL"
echo "PATH has $(echo $PATH | tr ':' '\n' | wc -l) directories"
# 8. Build a useful alias
alias myps='ps aux | grep -v grep | grep -E "PID|nginx|python|node"'
myps # test it
# 9. Real pipeline — analyze /etc/passwd
awk -F: '{print $7}' /etc/passwd | sort | uniq -c | sort -rn
# Shows which shells are in use and how many users have each

Essential Commands Quick Reference

Navigation:    pwd  ls  cd  tree
Viewing: cat less head tail tail -f wc
Creating: touch mkdir echo tee
Copying: cp cp -r cp -p
Moving: mv
Deleting: rm rm -rf rmdir
Searching: grep grep -r grep -E find locate
Text: sort uniq cut tr tac diff paste
Archives: tar -czf tar -xzf zip unzip
Processes: & nohup jobs fg bg kill
Pipelines: | && || ; xargs
Redirection: > >> 2> &> /dev/null tee <<EOF
Variables: export env echo $VAR unset
History: history !! sudo !! Ctrl+R
Shortcuts: Ctrl+A/E Alt+B/F Ctrl+W/K/U Ctrl+R

What’s Next

In Episode 04, we go deep on Users, Groups and Permissions — the full access control model that determines who can do what on a Linux system. We’ll cover user and group management, sudo configuration, the /etc/sudoers file, password policies, and the su command. After Episode 02's introduction to permissions, Episode 04 makes you fully fluent in managing them.

Episode Topic
EP 01 Introduction to Linux
EP 02 The Linux Filesystem
EP 03 ← You are here Essential Linux Commands
EP 04 Users, Groups & Permissions
EP 05 Shell & Bash Scripting
EP 06 Text Processing Power Tools
Bonus Vim: The Editor That Lives in Every Server
EP 07 Processes & Job Control
EP 08 Linux Networking Fundamentals
EP 09 Storage & Disk Management
EP 10 Package Management
EP 11 Linux Security Hardening
EP 12 Performance Monitoring & Troubleshooting

Before the Next Article

Three challenges to build muscle memory:

  1. Use only keyboard shortcuts for your next 15 minutes of terminal work. Ctrl+A, Ctrl+E, Alt+B, Alt+F instead of arrow keys. Ctrl+R instead of pressing up. Ctrl+W to delete words. It will feel slow at first. Push through.
  2. Build your personal alias set. Add at least 5 aliases to your ~/.bashrc that would genuinely save you time. source ~/.bashrc and use them immediately.
  3. Run the log analysis workflow from the “Processing a log file” section on any log file you have access to — /var/log/syslog, /var/log/auth.log, or an application log. Understand every part of the pipeline before moving on.

Commands are the vocabulary of Linux. The more naturally they come to you — the less you have to think about syntax and the more you can think about what you’re actually trying to accomplish — the more powerful you become as an engineer. See you in Episode 04.

← All articles Discuss on Medium ↗