- Published on
Mastering Linux Command Line: 100 Essential Commands Every Developer Must Know
- Authors

- Name
- Youngju Kim
- @fjvbn20031
1. Why the Linux Command Line?
The reasons developers should use the terminal over GUIs are clear: speed, automation, and remote server access. Over 90% of cloud servers run Linux, and CI/CD pipelines are entirely CLI-based. Without command line skills, you cannot do anything on a server.
What This Guide Covers
- 20 file system navigation and manipulation commands
- The holy trinity of text processing: grep, awk, sed with real examples
- Process and service management
- Network debugging and mastering SSH
- Bash scripting from basics to production
- tmux and Vim essentials
- System monitoring and troubleshooting
2. File System Navigation
2.1 Basic Navigation Commands
# Check current directory
pwd
# /home/developer/projects
# Change directory
cd /var/log # Absolute path
cd ../config # Relative path
cd ~ # Home directory
cd - # Previous directory
# List directory contents
ls -la # Detailed list including hidden files
ls -lhS # Sort by size (human-readable)
ls -lt # Sort by modification time
ls -R # Recursive listing
2.2 File Search
# find: Search for files in directory tree
find /var/log -name "*.log" -mtime -7 # .log files modified within 7 days
find . -type f -size +100M # Files larger than 100MB
find . -name "*.tmp" -exec rm -f {} \; # Find and delete .tmp files
find . -type f -name "*.js" ! -path "*/node_modules/*" # Exclude node_modules
# locate: Fast index-based search
sudo updatedb # Update database
locate nginx.conf # Quick file search
# which / whereis: Find executables
which python3 # /usr/bin/python3
whereis nginx # Binary, source, manual locations
# tree: Visualize directory structure
tree -L 2 -I "node_modules|.git" # 2 levels deep, exclude specific directories
2.3 Directory Management
# Create directories
mkdir -p project/src/components # Create intermediate directories
# Directory stack
pushd /etc/nginx # Save current location and navigate
# ... do work ...
popd # Return to saved location
dirs -v # View stack
3. File Manipulation
3.1 Copy, Move, Delete
# Copy
cp -r src/ backup/ # Recursive directory copy
cp -p important.conf important.conf.bak # Preserve permissions/timestamps
# Move/Rename
mv old_name.txt new_name.txt # Rename
mv *.log /var/log/archive/ # Move multiple files
# Delete (careful!)
rm -rf build/ # Force delete directory (use with caution)
rm -i *.tmp # Confirm before deleting
# Safer alternative
trash-put file.txt # Move to trash (trash-cli package)
3.2 File Permissions and Ownership
# Change permissions
chmod 755 script.sh # rwxr-xr-x
chmod +x deploy.sh # Add execute permission
chmod -R 644 public/ # Recursive permission change
# Change ownership
chown www-data:www-data /var/www # Change owner:group
chown -R developer: project/ # Recursive ownership change
# Understanding permission numbers
# r(4) + w(2) + x(1) = 7 (owner)
# r(4) + x(1) = 5 (group)
# r(4) + x(1) = 5 (others)
# Result: 755
3.3 Links and Archives
# Symbolic link (shortcut)
ln -s /usr/local/bin/python3.11 /usr/local/bin/python
ls -la /usr/local/bin/python # Verify link
# Hard link
ln original.txt hardlink.txt # Shares the same inode
# Archives and compression
tar -czf backup.tar.gz project/ # Create gzip compressed archive
tar -xzf backup.tar.gz # Extract
tar -xjf archive.tar.bz2 # Extract bzip2 archive
zip -r project.zip project/ -x "*/node_modules/*" # Zip excluding node_modules
unzip project.zip -d /tmp/ # Extract to specific directory
4. The Holy Trinity of Text Processing: grep, awk, sed
4.1 grep - Pattern Search
# Basic search
grep "ERROR" /var/log/syslog # Search lines containing ERROR
grep -i "warning" app.log # Case-insensitive
grep -r "TODO" src/ --include="*.py" # Recursive search in Python files
grep -n "function" script.js # Show line numbers
# Regular expressions
grep -E "^[0-9]{4}-[0-9]{2}" access.log # Lines starting with date pattern
grep -P "\d+\.\d+\.\d+\.\d+" access.log # IP address pattern (Perl regex)
grep -v "^#" config.ini # Exclude comments
# Practical: Check error context in logs
grep -B 3 -A 5 "Exception" app.log # Include surrounding lines
# Practical: Extract HTTP status codes
grep -oP 'HTTP/\d\.\d" \K5\d{2}' access.log | sort | uniq -c | sort -rn
4.2 awk - Text Processing Programming
# Field extraction
awk '{print $1, $4}' access.log # Print 1st and 4th fields
awk -F: '{print $1, $3}' /etc/passwd # Specify delimiter
# Conditional filtering
awk '$9 >= 500' access.log # HTTP 5xx errors only
awk '$3 > 1000 {print $1, $3}' data.txt # 3rd field greater than 1000
# Calculations
awk '{sum += $5} END {print "Total:", sum}' sales.txt # Sum
awk '{sum += $1; n++} END {print sum/n}' numbers.txt # Average
# Practical: Count requests per IP from logs
awk '{print $1}' access.log | sort | uniq -c | sort -rn | head -20
# Practical: CSV processing
awk -F',' '{
if ($3 > 100) {
total += $3
count++
}
}
END {
printf "Average: %.2f\n", total/count
}' sales.csv
# Practical: Filter logs by time range
awk '/2026-03-23 14:[0-5][0-9]/' application.log
4.3 sed - Stream Editor
# String substitution
sed 's/old/new/g' file.txt # Replace all old with new
sed -i 's/localhost/0.0.0.0/g' config.yml # Edit file in place (-i)
sed -i.bak 's/debug/info/g' app.conf # Create backup before editing
# Delete lines
sed '/^$/d' file.txt # Delete empty lines
sed '/^#/d' config.ini # Delete comment lines
sed '1,5d' file.txt # Delete lines 1-5
# Add/insert lines
sed '3a\New line after line 3' file.txt # Add after line 3
sed '1i\Header Line' file.txt # Insert before line 1
# Practical: Extract specific block from config file
sed -n '/\[database\]/,/\[/p' config.ini
# Practical: Multiple substitutions at once
sed -e 's/foo/bar/g' -e 's/baz/qux/g' -e '/^$/d' input.txt
4.4 Other Text Tools
# cut: Extract fields
cut -d: -f1,3 /etc/passwd # Extract fields by delimiter
cut -c1-10 file.txt # Extract by character position
# sort and uniq
sort -t: -k3 -n /etc/passwd # Sort by 3rd field numerically
sort -u file.txt # Sort + remove duplicates
uniq -c sorted.txt # Count duplicates
# wc: Count
wc -l *.py # Line count per file
find . -name "*.java" | xargs wc -l | tail -1 # Total Java line count
# head / tail
head -20 file.txt # First 20 lines
tail -f /var/log/syslog # Real-time log monitoring
tail -f app.log | grep --line-buffered "ERROR" # Real-time error filtering
# diff: Compare files
diff -u old.conf new.conf # Unified diff format
diff -rq dir1/ dir2/ # Compare directories (different files only)
# tr: Character translation
echo "Hello World" | tr 'A-Z' 'a-z' # Convert to lowercase
cat file.txt | tr -d '\r' # Remove Windows line endings
# xargs: Pass pipe results as arguments
find . -name "*.log" | xargs gzip # Compress found files
cat urls.txt | xargs -P 4 -I {} curl -sO {} # 4 parallel downloads
5. Process Management
5.1 Viewing Processes
# ps: Process list
ps aux # All processes
ps aux | grep nginx # Search specific process
ps -ef --forest # Process tree
# top / htop: Real-time monitoring
top -o %MEM # Sort by memory usage
htop # Interactive monitoring (more intuitive)
# pgrep / pidof: Find PIDs
pgrep -f "python app.py" # Search PID by name
pidof nginx # Get process PID
5.2 Process Control
# kill: Terminate processes
kill -15 1234 # SIGTERM (graceful shutdown request)
kill -9 1234 # SIGKILL (force kill)
killall nginx # Kill all by name
pkill -f "node server" # Kill by pattern match
# Background execution
nohup python server.py > output.log 2>&1 & # Survives logout
disown %1 # Detach from current shell
# jobs: Background job management
jobs -l # Current shell background jobs
fg %1 # Bring to foreground
bg %1 # Continue in background
5.3 systemd Service Management
# Service status/control
sudo systemctl status nginx # Check status
sudo systemctl start nginx # Start
sudo systemctl stop nginx # Stop
sudo systemctl restart nginx # Restart
sudo systemctl reload nginx # Reload config (no downtime)
sudo systemctl enable nginx # Auto-start on boot
# Service logs
journalctl -u nginx -f # Real-time logs
journalctl -u nginx --since "1 hour ago" # Last hour logs
journalctl -u nginx --no-pager -n 100 # Last 100 lines
journalctl -p err -b # Error logs from current boot only
# Service file location
systemctl show nginx -p FragmentPath
6. Network Commands
6.1 HTTP Requests and Downloads
# curl: HTTP requests
curl -s https://api.example.com/data # Basic GET
curl -X POST -H "Content-Type: application/json" \
-d '{"name":"test"}' https://api.example.com/users
curl -o file.zip https://example.com/file.zip # Download file
curl -I https://example.com # Headers only
curl -w "@curl-format.txt" -o /dev/null -s https://example.com # Measure response time
# wget: File downloads
wget -c https://example.com/large-file.iso # Resume download
wget -r -np -l 2 https://example.com/docs/ # Recursive download (2 levels)
6.2 Network Diagnostics
# Connection check
ss -tlnp # Check open TCP ports
ss -tlnp | grep :8080 # Find process using specific port
netstat -tulpn # netstat alternative (legacy)
# DNS lookup
dig example.com # DNS query
dig +short example.com A # Simple A record query
dig @8.8.8.8 example.com # Query specific DNS server
nslookup example.com # Simple DNS lookup
# Route tracing
traceroute example.com # Network path tracing
mtr example.com # Real-time traceroute + ping
# Firewall (iptables / nftables)
sudo iptables -L -n -v # View current rules
sudo iptables -A INPUT -p tcp --dport 80 -j ACCEPT # Allow HTTP port
sudo iptables -A INPUT -s 192.168.1.0/24 -j ACCEPT # Allow specific subnet
# Network interfaces
ip addr show # Show IP addresses
ip route show # Routing table
7. Mastering SSH
7.1 SSH Basics and Configuration
# Generate SSH key
ssh-keygen -t ed25519 -C "dev@example.com"
# Copy public key
ssh-copy-id -i ~/.ssh/id_ed25519.pub user@server
# SSH connection
ssh user@192.168.1.100
ssh -p 2222 user@server # Specific port
SSH config file makes connection management convenient.
# ~/.ssh/config
Host production
HostName 10.0.1.50
User deploy
Port 22
IdentityFile ~/.ssh/prod_key
ForwardAgent yes
Host staging
HostName 10.0.2.50
User deploy
IdentityFile ~/.ssh/staging_key
Host bastion
HostName bastion.example.com
User admin
IdentityFile ~/.ssh/bastion_key
Host internal-*
ProxyJump bastion
User developer
7.2 SSH Tunneling
# Local port forwarding: Access remote service locally
# Local port 5432 -> Remote server DB(5432)
ssh -L 5432:db-server:5432 bastion-server
# Reverse port forwarding: Access local service from remote
# Remote 8080 -> Local 3000
ssh -R 8080:localhost:3000 remote-server
# Dynamic port forwarding (SOCKS proxy)
ssh -D 1080 proxy-server
# Practical: Multiple tunnels at once
ssh -L 5432:db:5432 -L 6379:redis:6379 -L 9200:elastic:9200 bastion
7.3 File Transfer
# scp: Simple file transfer
scp local-file.txt user@server:/remote/path/
scp -r local-dir/ user@server:/remote/path/
scp user@server:/remote/file.txt ./local/
# rsync: Efficient synchronization (transfers only changes)
rsync -avz --progress local/ user@server:/remote/
rsync -avz --delete local/ user@server:/remote/ # Delete files not in source
rsync -avz --exclude "node_modules" --exclude ".git" \
project/ user@server:/deploy/
8. Bash Scripting
8.1 Basic Syntax
#!/bin/bash
set -euo pipefail # Exit on error, undefined variables, pipe failures
# Variables
APP_NAME="my-app"
VERSION="1.0.0"
TIMESTAMP=$(date +%Y%m%d_%H%M%S)
# String manipulation
echo "App: $APP_NAME, Version: $VERSION"
echo "Length: ${#APP_NAME}" # String length
echo "${APP_NAME^^}" # Uppercase (MY-APP)
echo "${APP_NAME/my/your}" # Substitution (your-app)
8.2 Conditionals and Loops
#!/bin/bash
# Conditionals
if [ -f "/etc/nginx/nginx.conf" ]; then
echo "Nginx config exists"
elif [ -f "/etc/apache2/apache2.conf" ]; then
echo "Apache config exists"
else
echo "No web server config found"
fi
# File test operators
# -f: File exists
# -d: Directory exists
# -r: Read permission
# -w: Write permission
# -x: Execute permission
# -s: File size greater than 0
# for loop
for server in web1 web2 web3; do
echo "Deploying to $server..."
ssh "$server" "cd /app && git pull && systemctl restart app"
done
# Iterate over files
for file in /var/log/*.log; do
echo "Processing: $file"
gzip "$file"
done
# while loop
while read -r line; do
echo "Processing: $line"
done < input.txt
# Counter loop
count=0
while [ $count -lt 10 ]; do
echo "Attempt $count"
((count++))
done
8.3 Functions and Error Handling
#!/bin/bash
set -euo pipefail
# Function definition
deploy() {
local environment="$1"
local version="$2"
echo "Deploying version $version to $environment..."
if ! docker pull "myapp:$version" 2>/dev/null; then
echo "Error: Image myapp:$version not found"
return 1
fi
docker stop myapp 2>/dev/null || true
docker run -d --name myapp "myapp:$version"
echo "Deploy complete!"
}
# Error handling
cleanup() {
echo "Cleaning up temporary files..."
rm -rf /tmp/deploy_*
}
trap cleanup EXIT # Always runs on script exit
# Argument parsing with getopts
usage() {
echo "Usage: deploy.sh -e ENVIRONMENT -v VERSION [-d]"
exit 1
}
DRY_RUN=false
while getopts "e:v:dh" opt; do
case $opt in
e) ENVIRONMENT="$OPTARG" ;;
v) VERSION="$OPTARG" ;;
d) DRY_RUN=true ;;
h) usage ;;
*) usage ;;
esac
done
# Execute
deploy "$ENVIRONMENT" "$VERSION"
8.4 Practical Script: Automated Backup
#!/bin/bash
set -euo pipefail
BACKUP_DIR="/backup"
DB_NAME="production"
RETENTION_DAYS=30
TIMESTAMP=$(date +%Y%m%d_%H%M%S)
BACKUP_FILE="$BACKUP_DIR/${DB_NAME}_$TIMESTAMP.sql.gz"
# Logging function
log() {
echo "[$(date '+%Y-%m-%d %H:%M:%S')] $1"
}
# Execute backup
log "Starting backup of $DB_NAME..."
pg_dump "$DB_NAME" | gzip > "$BACKUP_FILE"
# Verify backup
if [ -s "$BACKUP_FILE" ]; then
SIZE=$(du -h "$BACKUP_FILE" | cut -f1)
log "Backup successful: $BACKUP_FILE ($SIZE)"
else
log "ERROR: Backup file is empty!"
exit 1
fi
# Clean old backups
log "Removing backups older than $RETENTION_DAYS days..."
find "$BACKUP_DIR" -name "${DB_NAME}_*.sql.gz" -mtime +$RETENTION_DAYS -delete
# Check remaining backups
REMAINING=$(find "$BACKUP_DIR" -name "${DB_NAME}_*.sql.gz" | wc -l)
log "Done. $REMAINING backups remaining."
9. tmux - Terminal Multiplexer
9.1 Basic Usage
# Session management
tmux new -s dev # Create new session
tmux ls # List sessions
tmux attach -t dev # Attach to session
tmux kill-session -t dev # Kill session
9.2 Key Shortcuts
The default tmux prefix key is Ctrl+b.
| Action | Shortcut |
|---|---|
| Horizontal split | Ctrl+b % |
| Vertical split | Ctrl+b " |
| Navigate panes | Ctrl+b Arrow keys |
| New window | Ctrl+b c |
| Next window | Ctrl+b n |
| Previous window | Ctrl+b p |
| Detach session | Ctrl+b d |
| Resize pane | Ctrl+b Alt+Arrow keys |
| Zoom pane | Ctrl+b z |
9.3 Recommended tmux Configuration
# ~/.tmux.conf
set -g mouse on # Mouse support
set -g history-limit 50000 # Increase scrollback buffer
set -g base-index 1 # Window numbering from 1
setw -g pane-base-index 1 # Pane numbering from 1
# Change prefix key (Ctrl+a)
unbind C-b
set -g prefix C-a
# Intuitive split keys
bind | split-window -h -c "#{pane_current_path}"
bind - split-window -v -c "#{pane_current_path}"
# Vi mode
setw -g mode-keys vi
10. Vim Essentials
10.1 Modes and Basic Commands
| Mode | Enter | Description |
|---|---|---|
| Normal | Esc | Execute commands |
| Insert | i, a, o | Enter text |
| Visual | v, V | Select text |
| Command | : | Ex commands |
10.2 Survival Commands
# Navigation
h/j/k/l left/down/up/right
w / b word forward/backward
0 / $ line start/end
gg / G file start/end
:42 go to line 42
# Editing
dd delete line
yy yank (copy) line
p paste
u undo
Ctrl+r redo
. repeat last command
# Search/Replace
/pattern search forward
?pattern search backward
n / N next/previous result
:%s/old/new/g replace all
:%s/old/new/gc replace with confirmation
# Save/Quit
:w save
:q quit
:wq save and quit
:q! quit without saving
10.3 Practical Vim Tips
# Edit multiple files
:e filename open file
:bn / :bp next/previous buffer
:vs filename vertical split open
# Macros
qa start recording macro a
q stop recording
@a execute macro a
100@a execute 100 times
# Block editing (multi-line comments etc.)
Ctrl+v block select
I insert before block
Esc apply
11. System Monitoring
11.1 Memory and Disk
# Memory
free -h # Memory usage
cat /proc/meminfo # Detailed memory info
# Disk
df -h # Disk usage
du -sh /var/log/* # Size per directory
du -h --max-depth=1 / | sort -rh | head -10 # Largest directories
# Disk I/O
iostat -x 1 # I/O statistics (1 second interval)
iotop # Per-process I/O usage
11.2 CPU and System Performance
# CPU info
lscpu # CPU information
nproc # Number of CPU cores
uptime # Load average
# vmstat: Virtual memory statistics
vmstat 1 10 # 1 second interval, 10 times
# sar: System activity reporter
sar -u 1 5 # CPU usage (1 sec, 5 times)
sar -r 1 5 # Memory usage
sar -n DEV 1 5 # Network traffic
# lsof: List open files
lsof -i :8080 # Process using port 8080
lsof -u developer # Open files by user
lsof +D /var/log/ # Open files in directory
# strace: Trace system calls (debugging)
strace -f -e trace=network -p 1234 # Trace network-related system calls
12. Package Management
12.1 Debian/Ubuntu (apt)
sudo apt update # Update package list
sudo apt upgrade # Upgrade installed packages
sudo apt install nginx # Install package
sudo apt remove nginx # Remove package
sudo apt autoremove # Clean unnecessary packages
apt search keyword # Search packages
apt show nginx # Package info
dpkg -l | grep nginx # Check installed packages
12.2 RHEL/CentOS (yum/dnf)
sudo yum update # Update
sudo yum install httpd # Install
sudo yum remove httpd # Remove
yum search keyword # Search
rpm -qa | grep httpd # Check installed packages
12.3 macOS (brew)
brew update # Update Homebrew
brew install jq # Install package
brew upgrade # Upgrade all packages
brew list # List installed packages
brew cleanup # Clean cache
13. Docker CLI Essentials
# Image management
docker images # List images
docker pull nginx:latest # Pull image
docker build -t myapp:1.0 . # Build image
docker rmi myapp:1.0 # Remove image
# Container management
docker ps # Running containers
docker ps -a # All containers
docker run -d -p 8080:80 --name web nginx # Run in background
docker stop web # Stop container
docker rm web # Remove container
# Debugging
docker logs -f web # Real-time logs
docker exec -it web /bin/bash # Enter container shell
docker inspect web # Detailed information
# Cleanup
docker system prune -a # Remove all unused resources
docker volume prune # Remove unused volumes
14. Advanced Pipes and Redirects
# Basic redirects
command > output.txt # stdout to file (overwrite)
command >> output.txt # stdout to file (append)
command 2> error.log # stderr to file
command > output.txt 2>&1 # stdout + stderr to same file
command &> all.log # Same as above (Bash shorthand)
# Pipe chaining in practice
# Top 10 memory-consuming processes
ps aux --sort=-%mem | head -11
# Error frequency analysis from logs
cat app.log | grep "ERROR" | awk '{print $4}' | sort | uniq -c | sort -rn | head
# Real-time alert for specific pattern in logs
tail -f app.log | grep --line-buffered "CRITICAL" | while read line; do
echo "ALERT: $line" | mail -s "Critical Error" admin@example.com
done
# Process substitution
diff <(ssh server1 cat /etc/config) <(ssh server2 cat /etc/config)
# Here document
cat <<'SCRIPT' > /tmp/setup.sh
#!/bin/bash
echo "Running setup..."
apt update && apt install -y nginx
SCRIPT
15. 10 Troubleshooting Scenarios
Scenario 1: Disk Space Full
# 1. Check overall disk usage
df -h
# 2. Find large directories
du -h --max-depth=1 / 2>/dev/null | sort -rh | head -10
# 3. Find large files
find / -type f -size +500M 2>/dev/null | head -20
# 4. Check deleted files still held by processes
lsof +L1
# 5. Clean log files
sudo journalctl --vacuum-size=500M
find /var/log -name "*.gz" -mtime +30 -delete
Scenario 2: Service Not Responding
# 1. Check service status
sudo systemctl status myapp
# 2. Verify port listening
ss -tlnp | grep :8080
# 3. Check process status
ps aux | grep myapp
# 4. Check recent logs
journalctl -u myapp --since "10 minutes ago" --no-pager
# 5. Check resources (OOM Killer)
dmesg | grep -i "oom\|killed"
Scenario 3: High CPU Usage
# 1. Identify CPU-consuming processes
top -o %CPU -bn1 | head -15
# 2. Check threads of specific process
top -H -p $(pgrep myapp)
# 3. Trace system calls
strace -c -p $(pgrep myapp) -e trace=all
# 4. Load average history
sar -u 1 10
Scenario 4: Network Connection Issues
# 1. DNS check
dig api.example.com +short
# 2. Port connection test
nc -zv api.example.com 443
# 3. Route tracing
traceroute api.example.com
# 4. SSL certificate check
openssl s_client -connect api.example.com:443 -servername api.example.com </dev/null 2>/dev/null | openssl x509 -text -noout | grep -A2 "Validity"
Scenario 5: Memory Leak Investigation
# 1. Monitor memory usage trend
watch -n 5 'free -h'
# 2. Per-process memory usage
ps aux --sort=-%mem | head -10
# 3. Detailed memory for specific process
cat /proc/$(pgrep myapp)/status | grep -i "vm\|mem"
# 4. OOM Killer logs
dmesg -T | grep -i "oom"
Scenario 6: Slow Disk I/O
# 1. Check I/O wait
iostat -x 1 5
# 2. Find I/O heavy processes
iotop -oP
# 3. Disk performance test
dd if=/dev/zero of=testfile bs=1G count=1 oflag=direct
Scenario 7: SSH Connection Failure
# 1. Verbose debug mode
ssh -vvv user@server
# 2. SSH service status (on server)
sudo systemctl status sshd
# 3. Firewall check
sudo iptables -L -n | grep 22
# 4. Authentication log
sudo tail -50 /var/log/auth.log
Scenario 8: File Permission Issues
# 1. Check file permissions
ls -la /path/to/file
namei -l /path/to/file # Check entire path permissions
# 2. SELinux / AppArmor check
getenforce # SELinux status
ls -Z /path/to/file # SELinux context
# 3. ACL check
getfacl /path/to/file
Scenario 9: Attack Detection Through Log Analysis
# 1. Check abnormal login attempts
grep "Failed password" /var/log/auth.log | awk '{print $(NF-3)}' | sort | uniq -c | sort -rn | head
# 2. Web server abnormal access check
awk '$9 == 404' access.log | awk '{print $7}' | sort | uniq -c | sort -rn | head
# 3. Concurrent connection count
ss -tn | awk '{print $5}' | cut -d: -f1 | sort | uniq -c | sort -rn | head
Scenario 10: Zombie Process
# 1. Identify zombie processes
ps aux | awk '$8 ~ /Z/ {print}'
# 2. Find parent process
ps -o ppid= -p ZOMBIE_PID
# 3. Clean up by restarting parent
kill -SIGCHLD PARENT_PID
# Or restart the parent process
16. Useful One-Liners
# Top 10 largest files in current directory
find . -type f -exec du -h {} + | sort -rh | head -10
# Kill process using a specific port instantly
kill $(lsof -t -i:8080)
# Find largest files in git repository
git rev-list --objects --all | git cat-file --batch-check='%(objecttype) %(objectname) %(objectsize) %(rest)' | sed -n 's/^blob //p' | sort -snk2 | tail -10
# Measure server response time (10 iterations)
for i in $(seq 1 10); do curl -o /dev/null -s -w "%{time_total}\n" https://example.com; done
# Pretty print JSON
cat data.json | python3 -m json.tool
# or
cat data.json | jq .
# File encoding conversion
iconv -f EUC-KR -t UTF-8 input.txt > output.txt
# View all environment variables sorted
env | sort
# Total line count for specific file extension
find . -name "*.py" -exec wc -l {} + | tail -1
17. Interview Questions (15)
Q1. What is the difference between hard links and symbolic links?
Hard link: Points to the same inode. Accessible even if the original is deleted. Cannot cross filesystem boundaries. Cannot be used for directories.
Symbolic link: A separate inode pointing to a path. Becomes a broken link if the original is deleted. Can cross filesystems. Can be used for directories.
# Hard link: Same inode shared
ln original.txt hard.txt
ls -li original.txt hard.txt # Same inode number
# Symbolic link: Different inode, path reference
ln -s original.txt sym.txt
ls -li original.txt sym.txt # Different inode numbers
Q2. What is the difference between processes and threads?
Process: Has independent memory space, communicates via IPC (pipes, sockets). One process crash does not affect others.
Thread: Shares memory within the same process. Faster context switching, but one thread error can crash the entire process.
# Check processes with threads
ps -eLf | head # List including threads (LWP)
# NLWP column shows thread count
# Thread count for a specific process
ls /proc/PID/task/ | wc -l
Q3. What do file permissions 755 and 644 mean?
755: rwxr-xr-x - Owner (read/write/execute), group (read/execute), others (read/execute). Typically used for directories and executable files.
644: rw-r--r-- - Owner (read/write), group (read), others (read). Typically used for regular files.
Calculation: r=4, w=2, x=1. Sum for each position (owner/group/others).
Q4. What is the difference between SIGTERM and SIGKILL?
SIGTERM (15): Requests graceful shutdown. The process can perform cleanup in its signal handler (close files, delete temp files). Default for kill PID.
SIGKILL (9): Kernel immediately force-kills the process. Cannot be caught or handled. No cleanup operations, risk of data loss. Use only as last resort.
Q5. When should you use grep, awk, and sed?
- grep: Pattern searching (finding lines containing specific strings)
- awk: Field-based data processing (column extraction, calculations, conditional filtering)
- sed: Stream editing (string substitution, line deletion/insertion)
In practice, these three tools are often chained together with pipes:
# Analyze 5xx error URL frequency from logs
grep " 5[0-9][0-9] " access.log | awk '{print $7}' | sort | uniq -c | sort -rn | head
Q6. What is the difference between pipe (|) and redirect (>)?
Pipe (|): Connects stdout of one command to stdin of another. Inter-process communication.
Redirect (>): Sends command output to a file. Process-to-file communication.
# Pipe: ls output becomes grep input
ls -la | grep ".conf"
# Redirect: ls output goes to file
ls -la > filelist.txt
Q7. What is an inode?
An inode is a data structure in the filesystem that stores file metadata. It contains everything except the filename (owner, permissions, size, timestamps, data block locations). Directory entries map filenames to inode numbers.
# Check inode number
ls -i file.txt
stat file.txt # Detailed inode info
# Check inode usage (file count limit)
df -i
Q8. Explain the three types of SSH tunneling.
-
Local port forwarding (-L): Access remote service through local port.
ssh -L 5432:db:5432 bastion- Connecting to local 5432 reaches db's 5432 through bastion. -
Remote port forwarding (-R): Access local service from remote.
ssh -R 8080:localhost:3000 server- Connecting to server's 8080 reaches local 3000. -
Dynamic port forwarding (-D): Creates SOCKS proxy.
ssh -D 1080 server- Uses local 1080 as SOCKS proxy routing all traffic through server.
Q9. What is a zombie process and how do you handle it?
A zombie process is one that has finished execution but whose parent has not yet collected its exit status via the wait() system call. It only occupies a PID and consumes no resources, but too many can exhaust the PID table.
Solution: Send SIGCHLD to the parent process, or kill the parent so init/systemd adopts and cleans up the orphaned processes.
ps aux | awk '$8=="Z"' # Find zombie processes
kill -SIGCHLD PARENT_PID # Notify parent about child termination
Q10. What is the /proc filesystem?
/proc is a virtual filesystem that provides kernel and process information in file form. It does not physically exist on disk; the kernel generates it in real-time.
cat /proc/cpuinfo # CPU info
cat /proc/meminfo # Memory info
cat /proc/PID/status # Specific process status
cat /proc/PID/fd/ # Open file descriptors
cat /proc/loadavg # System load
Q11. What are the file descriptor numbers for stdin, stdout, and stderr?
- 0: stdin (standard input)
- 1: stdout (standard output)
- 2: stderr (standard error)
command > file.txt # Redirect fd 1 (stdout) to file
command 2> error.log # Redirect fd 2 (stderr) to file
command > file.txt 2>&1 # Redirect stderr to same place as stdout
command < input.txt # Read fd 0 (stdin) from file
Q12. What is the difference between cron and at?
cron: Used for recurring scheduled tasks. Managed via crontab files.
# minute hour day month weekday command
crontab -e
0 2 * * * /backup/run.sh # Every day at 2 AM
*/5 * * * * /check/health.sh # Every 5 minutes
at: Used for one-time tasks.
at now + 30 minutes
> /scripts/deploy.sh
> Ctrl+D
Q13. What is Load Average?
Load average is the average number of processes in runnable or waiting state. The uptime command shows 1-minute, 5-minute, and 15-minute averages.
Interpretation: Compare against CPU core count. On a 4-core system, load average 4.0 means 100% utilization, 8.0 means overloaded (processes waiting).
uptime # load average: 2.50, 3.10, 2.80
nproc # Check CPU core count
Q14. What is swap memory? Why should servers be careful with it?
Swap is disk space used as memory when RAM is exhausted. Disk I/O is thousands of times slower than RAM, so heavy swap usage causes severe performance degradation.
free -h # Check swap usage
swapon --show # Swap device info
vmstat 1 # High si/so values indicate heavy swap usage
cat /proc/sys/vm/swappiness # Swap tendency (0-100, lower prefers RAM)
Q15. What does set -euo pipefail do?
Safety guard for Bash scripts:
- -e (errexit): Script exits immediately when a command fails (non-zero exit)
- -u (nounset): Error on undeclared variable usage
- -o pipefail: Detects failures in middle of pipeline
Without these options, errors are silently ignored, leading to unexpected behavior. Always recommended for production scripts.
18. Quiz
Q1. What is the output of: echo "Hello" | tee output.txt | wc -c
Answer: 6
tee writes stdin to output.txt while also passing it to stdout. wc -c counts bytes: "Hello\n" = 6 bytes.
Q2. In chmod 4755 script.sh, what does the 4 mean?
Answer: SetUID bit
4 is the SetUID (Set User ID) bit. When executed, the file runs with the file owner's permissions. For example, /usr/bin/passwd is owned by root with SetUID set, allowing regular users to change passwords. SetGID is 2, Sticky Bit is 1.
Q3. Which is the correct way to monitor a file in real-time while filtering for a specific pattern?
A) grep "ERROR" < tail -f app.log
B) tail -f app.log | grep "ERROR"
C) tail -f app.log > grep "ERROR"
D) cat app.log | tail -f | grep "ERROR"
Answer: B
Pipe tail -f output to grep. A is a syntax error. C misuses redirect. D reads the whole file with cat first, making tail -f meaningless.
Q4. What actually happens with ssh -L 3306:db-server:3306 bastion?
Answer: Connecting to local port 3306 routes through the SSH connection via bastion server to reach db-server's port 3306 (MySQL). You can access the remote DB with mysql -h 127.0.0.1 -P 3306. Communication is encrypted via SSH.
Q5. Find the bug in this script.
#!/bin/bash
for f in $(ls *.txt); do
mv "$f" "${f%.txt}.md"
done
Answer: Using ls output in a for loop breaks when filenames contain spaces. The correct approach:
#!/bin/bash
for f in *.txt; do
[ -e "$f" ] || continue
mv "$f" "${f%.txt}.md"
done
Use glob patterns directly and check for existence when no files match.
19. References
Official Documentation
- GNU Coreutils Manual - Core utilities official docs
- GNU Bash Manual - Bash official reference
- The Linux Documentation Project - Comprehensive Linux documentation
- man7.org Linux Manual Pages - Online Linux man pages
- tmux Wiki - tmux official wiki
Learning Resources
- Linux Journey - Beginner-friendly Linux learning
- OverTheWire Bandit - Learn Linux through games
- Vim Adventures - Learn Vim through games
- ExplainShell - Enter a command and get each part explained
- ShellCheck - Bash script linter/analyzer
Books
- The Linux Command Line (William Shotts) - The Linux CLI bible
- Linux Pocket Guide (Daniel J. Barrett) - Quick reference
- Bash Cookbook (Carl Albing) - Bash recipe collection
- sed and awk (Dale Dougherty) - Advanced text processing
Cheat Sheets
- devhints.io/bash - Bash cheat sheet
- tmux Cheat Sheet - tmux shortcuts
- Vim Cheat Sheet - Vim commands reference