File System, Processes, Networking, Scripting, Systemd, Permissions — Linux mastery.
Core file manipulation commands. Every Linux user and administrator works with these daily.
# touch — create files / update timestamps
touch file.txt # create empty file
touch file1.txt file2.txt # create multiple files
touch -t 202501011200 file.txt # set specific timestamp
touch -a file.txt # update access time only
touch -m file.txt # update modification time only
touch -r ref.txt new.txt # use ref.txt's timestamp
# mkdir — create directories
mkdir project # single directory
mkdir -p src/components/ui # create parents as needed
mkdir -p -v a/b/c # verbose output
mkdir -m 750 secure_dir # set permissions at creation
mkdir {src,tests,docs} # brace expansion (3 dirs)
# cp — copy files and directories
cp file.txt backup.txt # copy file
cp file.txt /etc/backup/ # copy to directory
cp -r src/ backup/ # recursive copy (directories)
cp -a /etc/ /backup/etc/ # archive mode (preserve all metadata)
cp -p file.txt copy.txt # preserve mode, ownership, timestamps
cp -u source dest/ # copy only if source is newer
cp -v *.jpg /backup/images/ # verbose copy
cp -i file.txt backup/ # prompt before overwrite (alias safe)
cp --preserve=mode,ownership # preserve specific attributes
cp -r --update src/ dest/ # skip files that are newer in dest# mv — move or rename
mv old.txt new.txt # rename file
mv file.txt /tmp/ # move to directory
mv -i *.log /archive/ # interactive (prompt before overwrite)
mv -n file.txt /backup/ # no-clobber (don't overwrite)
mv -v *.jpg ./images/ # verbose
mv dir1/ dir2/ # rename directory
mv -T dir1/ dir2/ # treat destination as normal file
# rm — remove files or directories
rm file.txt # remove file
rm -r /tmp/build/ # recursive removal
rm -f *.o # force (ignore nonexistent, no prompt)
rm -rf /tmp/cache/ # recursive + force (dangerous!)
rm -i *.log # interactive (confirm each)
rm -v temp_* # verbose removal
rm -d empty_dir/ # remove empty directory
rm -- -filename # remove files starting with dash
# Safe alternatives
# Use trash-cli instead of rm for recoverable deletion
trash-put file.txt
trash-list
trash-restorerm -rf / can destroy your entire system. Always double-check paths before using rm -rf. Consider setting alias rm='rm -i' for interactive prompts.# cat — concatenate and display
cat file.txt
cat -n file.txt # show line numbers
cat -b file.txt # number non-empty lines
cat -s file.txt # squeeze blank lines
cat -A file.txt # show all invisible chars ($, ^I, M-)
cat file1.txt file2.txt > combined.txt
# less — pager with scrolling (preferred)
less /var/log/syslog
less -N /var/log/syslog # show line numbers
less -S file.csv # don't wrap long lines
less +F /var/log/syslog # follow mode (like tail -f)
less -i file.txt # case-insensitive search
less -R file.txt # preserve color codes
# Inside less: /search, n/N, G/g, q, F, v
# more — simple pager
more file.txt # space to page, q to quit
# head — first lines
head -n 20 file.txt # first 20 lines
head -5 file.txt # first 5 lines (shorthand)
head -c 1024 file.txt # first 1024 bytes
# tail — last lines
tail -n 50 file.txt # last 50 lines
tail -f /var/log/syslog # follow (live streaming)
tail -F /var/log/syslog # follow + retry on rotation
tail -n +100 file.txt # from line 100 to end
tail -f access.log | grep 404 # follow + filter# tee — read stdin and write to stdout + files
ls -la | tee listing.txt # display and save
echo "data" | tee -a log.txt # append mode
command | tee /dev/tty | grep error # see output + pipe
sudo tee /etc/config.txt < local.txt # write with sudo
# wc — word count
wc file.txt # lines, words, bytes
wc -l file.txt # lines only
wc -w file.txt # words only
wc -c file.txt # bytes only
wc -m file.txt # characters only
wc -L file.txt # longest line length
cat *.txt | wc -l # total lines across files
# sort — sort lines
sort file.txt # alphabetical
sort -n numbers.txt # numeric sort
sort -r file.txt # reverse
sort -k2 file.csv # sort by 2nd field
sort -t: -k3n /etc/passwd # sort passwd by UID (colon delimiter)
sort -u file.txt # unique lines (deduplicate)
sort -h sizes.txt # human-readable (1K, 2M, 3G)
sort -V versions.txt # version sort (1.10 > 1.2)
sort -b -f file.txt # ignore leading blanks, case-insensitive
# uniq — unique lines (requires sorted input)
sort file.txt | uniq # remove adjacent duplicates
sort file.txt | uniq -c # prefix with count
sort file.txt | uniq -d # only duplicate lines
sort file.txt | uniq -u # only unique lines
sort file.txt | uniq -w 10 # compare first 10 chars
# diff — compare files
diff file1.txt file2.txt # show differences
diff -u file1.txt file2.txt # unified format (preferred)
diff -r dir1/ dir2/ # recursive directory diff
diff -q dir1/ dir2/ # quick: are they different?
diff --color file1 file2 # colored output
diff -y file1.txt file2.txt # side-by-side
diff -w file1 file2 # ignore whitespace
# patch — apply diff output
diff -u original.txt modified.txt > fix.patch
patch original.txt < fix.patch # apply patch
patch -p1 < fix.patch # strip first path component
patch --dry-run < fix.patch # test without applying
patch -R < fix.patch # reverse/undo patch# chmod — change file mode (permissions)
chmod 755 script.sh # rwxr-xr-x
chmod 644 file.txt # rw-r--r--
chmod 700 private/ # rwx------
chmod +x script.sh # add execute for all
chmod u+x script.sh # add execute for user
chmod g+w file.txt # add write for group
chmod o-r file.txt # remove read for others
chmod a+r file.txt # add read for all
chmod u=rwx,go=rx file.txt # set explicitly
chmod -R 755 /var/www/ # recursive
chmod go-w file.txt # remove write for group+other
# chown — change file owner
sudo chown user file.txt
sudo chown user:group file.txt # owner + group
sudo chown :group file.txt # group only
sudo chown -R user:group /var/www/ # recursive
sudo chown --reference=ref.txt target.txt
# chgrp — change group ownership
chgrp developers file.txt
chgrp -R www-data /var/www/
# umask — default permission mask
umask # show current (e.g., 0022)
umask -S # symbolic output (u=rwx,g=rx,o=rx)
umask 022 # new files: 644, dirs: 755
umask 077 # private: files 600, dirs 700
# Formula: file = 666 - umask, dir = 777 - umask# xargs — convert stdin to command arguments
find . -name "*.log" | xargs rm # delete found files
find . -name "*.py" | xargs wc -l # count lines in Python files
echo "file1 file2 file3" | xargs touch # create multiple files
# Important flags
find . -name "*.jpg" | xargs -I {} cp {} /backup/
# -I {} replaces {} with each argument
find . -print0 | xargs -0 rm -rf
# -print0 / -0 handle filenames with spaces/newlines
echo "1 2 3 4 5" | xargs -n 2 # 2 args per command invocation
find . -type f | xargs -P 4 gzip # run 4 processes in parallel
find . -type f | xargs -p rm # prompt before each executionfind ... -print0 | xargs -0 instead of plain find ... | xargs to safely handle filenames with spaces, quotes, or newlines.Monitor, control, and manage running processes. Essential for system administration and debugging.
# ps — report process status
ps # processes in current terminal
ps aux # all processes (BSD style)
ps -ef # all processes (UNIX style)
ps -eF # extra full format
ps -eo pid,ppid,cmd,%mem,%cpu # select specific columns
ps -u nginx # processes by user
ps -p 1234,5678 # specific PIDs
ps --forest # tree view
ps -ejH # process tree with session
# Common ps aux columns:
# USER, PID, %CPU, %MEM, VSZ, RSS, TTY, STAT, START, TIME, COMMAND
# STAT: S=sleeping, R=running, Z=zombie, T=stopped, D=uninterruptible sleep
# s=session leader, l=multi-threaded, +=foreground process group# top — interactive process viewer
top # default view (sorted by CPU)
top -d 2 # refresh every 2 seconds
top -o %MEM # sort by memory
top -p 1234 # monitor specific PID
top -u nginx # processes by user
top -b -n 1 > snapshot.txt # batch mode (one iteration)
# Inside top: q=quit, k=kill, r=renice, h=help, M=sort mem, P=sort cpu
# htop — improved interactive viewer
htop # colorful, scrollable
# Inside htop:
# F1=help, F2=setup, F3/F4=search/filter
# F5=tree, F6=sort, F7/F8=nice +/-, F9=kill
# F10=quit, / = filter, s = trace (strace)
# l = show open files (lsof), t = tree view# kill — send signal to process by PID
kill 1234 # SIGTERM (15) — polite termination
kill -9 1234 # SIGKILL (9) — force kill
kill -15 1234 # SIGTERM explicitly
kill -1 1234 # SIGHUP — reload config (many daemons)
kill -SIGTERM 1234 # named signal
kill -l # list all signals
# Common signals:
# SIGHUP(1) SIGINT(2) SIGQUIT(3) SIGKILL(9) SIGTERM(15)
# SIGCONT(18) SIGSTOP(19) SIGUSR1(10) SIGUSR2(12)
# killall — kill by process name
killall nginx # kill all nginx processes
killall -9 chrome # force kill all chrome
killall -u alice # kill all processes by user
killall -r "python.*manage" # regex match
# pkill — kill by pattern
pkill -f "node server.js" # match full command line
pkill -u deploy # kill all processes by user
pkill -9 -f "celery" # force kill matching pattern
pkill --signal USR1 nginx # send specific signal
# pgrep — find processes by pattern
pgrep nginx # list PIDs matching "nginx"
pgrep -f "python app.py" # match full command
pgrep -u www-data # by user
pgrep -l nginx # show name with PID
pgrep -a nginx # show full command line
pgrep -P 1234 # children of PID 1234
pgrep -d',' nginx # comma-separated output
# pstree — process tree
pstree # tree of all processes
pstree -p # show PIDs
pstree -p 1234 # tree for specific PID
pstree -u # show user ownership
pstree -agn # all info + command args# & — run in background
python server.py & # background process
command > output.log 2>&1 & # background with output redirect
# jobs — list background jobs
jobs # all background jobs
jobs -l # with PIDs
jobs -p # PIDs only
# Ctrl+Z — suspend current foreground process
# bg — resume in background
# fg — bring to foreground
bg %1 # resume job 1 in background
fg %1 # bring job 1 to foreground
fg %- # bring last job to foreground
# kill background jobs
kill %1 # kill job 1
kill -9 %2 # force kill job 2
# nohup — run immune to hangups
nohup python server.py & # survive terminal close
nohup ./build.sh > build.log 2>&1 & # capture all output
# disown — detach from shell
command &
disown # remove from job table
disown -h %1 # keep job but ignore SIGHUP
disown -a # disown all jobs
disown -r # disown all running jobs# /proc provides kernel/process information as files
# Process-specific (PID-based)
cat /proc/1/cmdline | tr '\0' ' ' # command line of PID 1
cat /proc/1/status # process status info
cat /proc/1/environ | tr '\0' '\n' # environment variables
cat /proc/1/fd # list open file descriptors
cat /proc/1/limits # resource limits
cat /proc/1/maps # memory mappings
ls -la /proc/1/fd/ # file descriptor targets
cat /proc/1/io # I/O statistics
# System-wide
cat /proc/cpuinfo # CPU information
cat /proc/meminfo # memory information
cat /proc/loadavg # load averages (1, 5, 15 min)
cat /proc/uptime # system uptime (seconds)
cat /proc/version # kernel version
cat /proc/partitions # partition information
cat /proc/interrupts # IRQ info
cat /proc/sys/kernel/pid_max # max PID value
cat /proc/sys/vm/swappiness # swap tendency
cat /proc/net/tcp # TCP connections# nice — start process with priority (-20 highest, 19 lowest)
nice -n 10 command # lower priority (niceness 10)
nice -n -5 command # higher priority (needs root for < 0)
nice command # default niceness 10
# renice — change priority of running process
renice 10 -p 1234 # set PID 1234 to niceness 10
renice -n -5 -p 1234 # same as above
renice 5 -u alice # all processes by user
sudo renice -n -10 -p 1234 # increase priority (needs sudo)
# ionice — I/O scheduling priority
ionice -c 2 -n 7 command # best-effort, lowest I/O priority
ionice -c 3 command # idle (only uses I/O when disk idle)
ionice -p 1234 # show current I/O class
ionice -c 1 -p 1234 # realtime I/O class
# I/O classes:
# 1 = realtime (be careful!)
# 2 = best-effort (0-7 priority, default)
# 3 = idle (background tasks)# systemctl — service management
sudo systemctl start nginx # start service
sudo systemctl stop nginx # stop service
sudo systemctl restart nginx # restart
sudo systemctl reload nginx # reload config (no downtime)
sudo systemctl enable nginx # enable at boot
sudo systemctl disable nginx # disable at boot
sudo systemctl status nginx # show status
systemctl is-active nginx # check if running
systemctl is-enabled nginx # check if enabled
systemctl list-units --type=service # all services
systemctl list-units --state=failed # failed services
systemctl --failed # same as above
# systemctl — system state
systemctl reboot # reboot
systemctl poweroff # power off
systemctl suspend # suspend
systemctl hibernate # hibernate
systemctl get-default # show default target
sudo systemctl isolate multi-user.target # switch runlevel
systemctl list-dependencies nginx # dependency tree
# journalctl — log viewer
journalctl # all logs (newest first)
journalctl -u nginx # logs for specific unit
journalctl -u nginx --since today # since today
journalctl -u nginx --since "2025-01-01" --until "2025-01-02"
journalctl -f # follow (live logs)
journalctl -f -u nginx # follow specific unit
journalctl --disk-usage # disk usage of logs
journalctl --vacuum-time=7d # delete logs older than 7 days
journalctl --vacuum-size=100M # limit log size
journalctl -p err # messages with priority err+
journalctl -b # current boot only
journalctl -b -1 # previous boot
journalctl --no-pager # no pager (pipe-friendly)
journalctl -o json # JSON output
journalctl -o json-pretty # pretty JSON
# System service file example
# /etc/systemd/system/myservice.service[Unit]
Description=My Custom Service
After=network.target
Wants=network-online.target
[Service]
Type=simple
User=myapp
Group=myapp
WorkingDirectory=/opt/myapp
ExecStart=/opt/myapp/bin/start.sh
ExecReload=/bin/kill -HUP $MAINPID
Restart=on-failure
RestartSec=5
Environment=NODE_ENV=production
EnvironmentFile=/opt/myapp/.env
StandardOutput=journal
StandardError=journal
SyslogIdentifier=myservice
[Install]
WantedBy=multi-user.target# Timer unit (cron replacement)
# /etc/systemd/system/backup.timer[Unit]
Description=Daily Backup Timer
[Timer]
OnCalendar=daily
Persistent=true
AccuracySec=1h
[Install]
WantedBy=timers.target# Manage timers
systemctl list-timers # show all timers
systemctl start backup.timer
systemctl enable backup.timer# crontab — manage user cron jobs
crontab -e # edit crontab
crontab -l # list current jobs
crontab -r # remove all jobs
crontab -u alice -l # view another user's crontab
# Crontab format:
# MIN HOUR DOM MONTH DOW COMMAND
# 0-59 0-23 1-31 1-12 0-6 (0=Sunday)
# Special: * = any, */n = every n, , = list, - = range
# Examples
0 * * * * /opt/script/hourly.sh # every hour
*/15 * * * * /opt/script/check.sh # every 15 minutes
0 2 * * * /opt/scripts/backup.sh # 2 AM daily
0 0 * * 0 /opt/scripts/weekly.sh # midnight every Sunday
0 0 1 * * /opt/scripts/monthly.sh # midnight on 1st of month
30 4 * * 1-5 /opt/scripts/weekday.sh # 4:30 AM, Mon-Fri
# cron shortcuts
@reboot /opt/scripts/startup.sh # at system boot
@daily /opt/scripts/daily.sh # midnight daily
@weekly /opt/scripts/weekly.sh # midnight Sunday
@hourly /opt/scripts/hourly.sh # every hour
# System cron directories
# /etc/cron.hourly/ — hourly scripts
# /etc/cron.daily/ — daily scripts
# /etc/cron.weekly/ — weekly scripts
# /etc/cron.monthly/ — monthly scripts
# Redirect cron output
0 2 * * * /opt/backup.sh >> /var/log/backup.log 2>&1
MAILTO=admin@example.com # send output via emailsystemd-run --on-calendar="daily" /opt/script.sh as a quick alternative to writing a full timer unit. For persistent scheduled tasks, prefer systemd timers over cron — they provide logging, dependency management, and better error handling.Network configuration, diagnostics, and remote access. These commands are critical for server management and troubleshooting.
# ip addr — manage network interfaces
ip addr show # all interfaces
ip addr show eth0 # specific interface
ip addr add 192.168.1.10/24 dev eth0 # add IP
ip addr del 192.168.1.10/24 dev eth0 # remove IP
ip addr flush dev eth0 # flush all IPs on interface
ip -brief addr # concise output
ip -color addr # colored output
# ip link — manage network devices
ip link show # list interfaces
ip link set eth0 up # bring interface up
ip link set eth0 down # bring interface down
ip link set eth0 promisc on # enable promiscuous mode
ip link show eth0 # interface details
# ip route — routing table
ip route show # show routing table
ip route add default via 192.168.1.1 # add default gateway
ip route add 10.0.0.0/24 via 192.168.1.1 # static route
ip route del 10.0.0.0/24 # delete route
ip route get 8.8.8.8 # show route to specific host
ip route flush cache # flush routing cache# ping — test network connectivity
ping 8.8.8.8 # IPv4
ping -c 4 google.com # send 4 packets
ping -i 0.5 8.8.8.8 # interval 0.5 seconds
ping -W 3 8.8.8.8 # timeout 3 seconds
ping -s 1500 8.8.8.8 # packet size 1500 bytes
ping6 2001:4860:4860::8888 # IPv6
# traceroute — trace packet route
traceroute google.com # default (UDP)
traceroute -I google.com # use ICMP
traceroute -T -p 443 google.com # use TCP on port 443
traceroute -n google.com # numeric only (no DNS lookup)
traceroute -w 2 google.com # wait max 2s per hop
tracepath google.com # similar (no root needed)
# curl — transfer data (swiss army knife)
curl https://api.example.com # GET request
curl -o output.html https://... # save to file
curl -O https://example.com/file.tar.gz # save with remote name
curl -I https://example.com # HEAD only (headers)
curl -L https://example.com # follow redirects
curl -s https://api.com/data # silent mode
curl -v https://api.com/data # verbose
curl -X POST https://api.com/data -H "Content-Type: application/json" \
-d '{"key": "value"}' # POST with JSON body
curl -X PUT -H "Authorization: Bearer TOKEN" \
-d '{"field":"update"}' URL # PUT with auth header
curl -F "file=@/path/to/file" https://upload.com # upload file
curl -x http://proxy:8080 URL # use HTTP proxy
curl --connect-timeout 10 URL # connection timeout
curl --max-time 30 URL # max total time
curl --retry 3 --retry-delay 5 URL # retry on failure
curl -H "Accept: application/json" URL # set headers
curl -b cookies.txt URL # send cookies
curl -c cookies.txt URL # save cookies
curl -sSL https://raw.githubusercontent.com/user/repo/main/install.sh | bash
# wget — download files
wget https://example.com/file.zip # download
wget -O custom_name.zip https://... # rename output
wget -q https://example.com/file.zip # quiet mode
wget -c https://example.com/large.iso # continue partial download
wget --limit-rate=1M https://... # throttle download speed
wget -b https://example.com/large.iso # background download
wget --mirror https://example.com # mirror website
wget -r -np -p https://example.com/docs/ # recursive download
wget --spider https://example.com # check if URL exists# ssh — secure shell
ssh user@server # basic connection
ssh -p 2222 user@server # custom port
ssh -i ~/.ssh/mykey.pem user@server # specific key
ssh -v user@server # verbose (debug connection)
ssh -X user@server # X11 forwarding
ssh -L 8080:localhost:80 user@server # local port forwarding
ssh -R 3000:localhost:3000 user@server # remote port forwarding
ssh -D 1080 user@server # SOCKS proxy on port 1080
ssh -J bastion user@internal # jump host / proxy jump
ssh -o StrictHostKeyChecking=no user@server # skip host key check
ssh -t user@server "tmux attach" # force pseudo-terminal
# SSH key management
ssh-keygen -t ed25519 -C "email@example.com" # generate key
ssh-keygen -t rsa -b 4096 # RSA 4096-bit
ssh-copy-id user@server # copy key to server
ssh-add ~/.ssh/mykey # add key to agent
ssh-agent bash # start SSH agent
# scp — secure copy
scp file.txt user@server:/tmp/ # upload file
scp user@server:/var/log/syslog . # download file
scp -r ./src user@server:/opt/app/ # recursive copy
scp -P 2222 file.txt user@server:/tmp/ # custom port
scp -i ~/.ssh/key.pem file.txt user@server:~/
# sftp — interactive secure FTP
sftp user@server
# sftp commands: get, put, cd, lcd, ls, lls, mkdir, rmdir, rm, pwd, bye
# rsync — fast incremental file transfer
rsync -avz ./src/ user@server:/opt/app/ # archive, verbose, compress
rsync -avz --progress source/ dest/ # show progress
rsync -avz --delete source/ dest/ # delete extraneous files in dest
rsync -avz --exclude='.git' ./ user@server:~/repo/
rsync -avz --dry-run source/ dest/ # simulation (no changes)
rsync -avz -e "ssh -p 2222" source/ dest/ # custom SSH port
rsync -az --partial --progress large.iso dest/ # resume partial transfer
rsync -avz --backup --backup-dir=/backup/ source/ dest/StrictHostKeyChecking=no in production scripts. Always verify host keys. For automation, use ssh-keyscan to pre-populate known_hosts.# netstat — network statistics (legacy)
netstat -tlnp # TCP listening ports
netstat -ulnp # UDP listening ports
netstat -an # all connections, numeric
netstat -rn # routing table
netstat -s # statistics summary
netstat -p # show PID/program name
# ss — socket statistics (modern replacement)
ss -tlnp # TCP listening with PIDs
ss -ulnp # UDP listening with PIDs
ss -tan # all TCP connections, numeric
ss -s # socket statistics summary
ss -p # show process info
ss -tn state established # established connections only
ss -tn dst :443 # connections to port 443
ss -tn src 192.168.1.0/24 # from specific subnet
ss -tn sport = :22 # source port 22
ss -o state established '( dport = :443 or dport = :80 )'
# lsof — list open files (including network)
lsof -i :80 # processes on port 80
lsof -i :443 # processes on port 443
lsof -i TCP # all TCP connections
lsof -i UDP # all UDP connections
lsof -i @192.168.1.1 # connections to host
lsof -u nginx # files opened by user
lsof -p 1234 # files opened by PID
lsof +D /var/log # all processes using dir
lsof /var/log/syslog # processes using specific file
lsof -c nginx # processes matching command name# iptables — IPv4 packet filtering
# View rules
sudo iptables -L -n -v # list all rules
sudo iptables -L INPUT -n -v # INPUT chain rules
sudo iptables -S # list rules in format
# Basic rules
sudo iptables -A INPUT -p tcp --dport 22 -j ACCEPT # allow SSH
sudo iptables -A INPUT -p tcp --dport 80 -j ACCEPT # allow HTTP
sudo iptables -A INPUT -p tcp --dport 443 -j ACCEPT # allow HTTPS
sudo iptables -A INPUT -i lo -j ACCEPT # allow loopback
sudo iptables -A INPUT -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT
sudo iptables -P INPUT DROP # default deny INPUT
sudo iptables -P FORWARD DROP # default deny FORWARD
sudo iptables -A INPUT -s 192.168.1.0/24 -j ACCEPT # allow subnet
# Delete rules
sudo iptables -D INPUT -p tcp --dport 80 -j ACCEPT
sudo iptables -F # flush all rules
sudo iptables -X # delete all custom chains
# Save/restore
sudo iptables-save > /etc/iptables.rules
sudo iptables-restore < /etc/iptables.rules
# nftables — modern replacement for iptables
sudo nft list ruleset # show all rules
sudo nft add table inet filter
sudo nft add chain inet filter input { type filter hook input priority 0 \; }
sudo nft add rule inet filter input tcp dport { 22, 80, 443 } accept
sudo nft add rule inet filter input iif lo accept
sudo nft add rule inet filter input counter accept
sudo nft list table inet filter
# nmap — network port scanner
nmap 192.168.1.1 # scan single host
nmap -p 22,80,443 server.com # scan specific ports
nmap -p- server.com # scan all 65535 ports
nmap -sV server.com # detect service versions
nmap -O server.com # detect OS (needs root)
nmap -sC server.com # use default scripts
nmap -A server.com # OS + version + scripts + traceroute
nmap -sn 192.168.1.0/24 # ping sweep (discover hosts)
nmap -sU 192.168.1.1 # UDP scan
nmap -T4 server.com # aggressive timing
nmap --top-ports 1000 server.com # scan top 1000 ports# dig — DNS lookup (most flexible)
dig example.com # A record query
dig example.com ANY # all records
dig example.com MX # mail exchange records
dig example.com NS # name server records
dig example.com TXT # TXT records
dig +short example.com # short answer only
dig +trace example.com # trace DNS resolution path
dig @8.8.8.8 example.com # query specific DNS server
dig -x 8.8.8.8 # reverse DNS (PTR record)
dig +dnssec example.com # with DNSSEC validation
dig +multiline example.com # formatted output
# nslookup — simpler DNS lookup
nslookup example.com
nslookup -type=MX example.com
nslookup -type=NS example.com
nslookup 8.8.8.8 # reverse lookup
nslookup example.com 8.8.8.8 # use specific server
# host — concise DNS lookup
host example.com
host -t MX example.com
host -t NS example.com
host 8.8.8.8 # reverse lookup# /etc/hosts — static hostname resolution127.0.0.1 localhost
127.0.1.1 myserver
::1 localhost ip6-localhost ip6-loopback
192.168.1.10 dev-server
10.0.0.5 db.internal # internal database# /etc/resolv.conf — DNS resolver configurationnameserver 8.8.8.8
nameserver 8.8.4.4
nameserver 1.1.1.1
search example.com internal
options timeout:2 attempts:3 rotate# /etc/nsswitch.conf — name service switch
# Determines order: files (hosts), dns, mdns4_minimal
# nmcli — NetworkManager CLI
nmcli device status # show all devices
nmcli connection show # show connections
nmcli device wifi list # list WiFi networks
nmcli device wifi connect SSID password "PASS"
nmcli connection up "Wired connection 1"
nmcli connection down "Wired connection 1"
nmcli general status # overall status
nmcli connection modify "conn" ipv4.addresses 192.168.1.10/24
nmcli device show eth0 # device details
# hostname
hostname # show hostname
hostname -f # FQDN
hostname -I # all IP addresses
sudo hostnamectl set-hostname myserver # set hostname permanently
hostnamectl status # detailed hostname infoss -tlnp instead of netstat -tlnp — it's faster, has more filtering options, and is the modern standard. Similarly, prefer ip addr over ifconfig.Linux permissions model including traditional Unix permissions, special bits, ACLs, capabilities, and mandatory access control systems.
| Numeric | Symbolic | Description |
|---|---|---|
| 7 | rwx | Read (4) + Write (2) + Execute (1) |
| 6 | rw- | Read (4) + Write (2) |
| 5 | r-x | Read (4) + Execute (1) |
| 4 | r-- | Read only (4) |
| 0 | --- | No permissions |
# Numeric mode (owner | group | others)
chmod 755 script.sh # rwxr-xr-x (owner: full, group/other: read+exec)
chmod 644 file.txt # rw-r--r-- (owner: read+write, group/other: read)
chmod 600 secret.key # rw------- (owner: read+write only)
chmod 700 .ssh/ # rwx------ (private directory)
chmod 1777 /tmp # rwxrwxrwt (sticky bit: only owner can delete)
chmod 2755 /usr/local/bin # rwxr-sr-x (setgid bit)
chmod 4755 /usr/bin/sudo # rwsr-xr-x (setuid bit)
# Symbolic mode
chmod u+x script.sh # add execute for user
chmod g-w file.txt # remove write for group
chmod o=rx file.txt # set exactly read+execute for others
chmod a+r file.txt # add read for all (user, group, other)
chmod u=rwx,go= file.txt # full for owner, nothing for group/other
chmod +x script.sh # add execute for all (when no ugo specified)# setuid (SUID) — run file as the owner
# Applied to executables; process runs with owner's privileges
chmod u+s /usr/bin/passwd # example: passwd runs as root
chmod 4755 /path/to/program # numeric: 4xxx sets SUID
# Display: rwsr-xr-x (s replaces x in owner position)
# setgid (SGID) — run as group / inherit group for new files
chmod g+s /usr/local/bin/ # on directory: new files inherit group
chmod 2755 /path/to/dir # numeric: 2xxx sets SGID
# Display: rwxr-sr-x (s replaces x in group position)
# Sticky bit — restrict deletion to owner (on directories)
chmod +t /tmp # only file owner can delete their files
chmod 1777 /shared/ # numeric: 1xxx sets sticky bit
# Display: rwxrwxrwt (t replaces x in others position)
# Common sticky bit directories: /tmp, /var/tmp
# Common SUID binaries: /usr/bin/passwd, /usr/bin/sudo, /usr/bin/ping
# Common SGID directories: /usr/local/bin (shared group dirs)find / -perm /6000 -type f 2>/dev/null to audit all SUID/SGID files. SUID root programs are common attack vectors.# ACLs extend standard permissions with per-user/per-group rules
# Requires filesystem with ACL support (ext4, xfs default)
# getfacl — view ACLs
getfacl file.txt
# output: user::rw-, user:alice:rw-, group::r--, other::r--
# setfacl — set ACLs
setfacl -m u:alice:rw file.txt # give alice read+write
setfacl -m u:bob:r file.txt # give bob read only
setfacl -m g:developers:rw file.txt # give developers group rw
setfacl -m d:u:alice:rw dir/ # default ACL for new files in dir
setfacl -x u:alice file.txt # remove alice's ACL
setfacl -b file.txt # remove ALL ACLs (back to basic perms)
# Mask and effective permissions
# The ACL mask limits the effective permissions of named users and groups
# Always check effective permissions: getfacl shows #effective: field
setfacl -m m::rw file.txt # set ACL mask
# Backup and restore ACLs
getfacl -R /path/to/dir > acl_backup
setfacl --restore=acl_backup
setfacl -R -m u:deployer:rwx /var/www/app/# Linux Capabilities — fine-grained root privilege splitting
# Instead of full SUID root, grant specific capabilities
# List capabilities
getcap /usr/bin/ping # cap_net_raw=ep
getcap -r /usr/bin 2>/dev/null # recursive (all binaries)
# Set capabilities
sudo setcap cap_net_raw+ep /usr/bin/ping
sudo setcap cap_net_bind_service+ep /usr/bin/node # bind to ports < 1024
# Remove capabilities
sudo setcap -r /usr/bin/ping
# Common capabilities:
# cap_net_raw — use raw sockets (ping)
# cap_net_bind_service — bind to ports < 1024
# cap_sys_admin — many admin operations
# cap_dac_override — bypass file permission checks
# cap_setuid/cap_setgid — change UID/GID
# ---- SELinux (Red Hat / CentOS / Fedora) ----
# Check SELinux status
getenforce # Enforcing, Permissive, or Disabled
sestatus # detailed status
cat /etc/selinux/config # persistent config
# SELinux context operations
ls -Z /var/www/html/ # view SELinux contexts
ps -Z # process contexts
id -Z # current user context
chcon -R -t httpd_sys_content_t /var/www/html/ # change context
restorecon -R -v /var/www/html/ # reset to default contexts
semanage fcontext -a -t httpd_sys_content_t "/web(/.*)?"
restorecon -R -v /web/
# SELinux booleans
getsebool -a | grep httpd # list httpd-related booleans
setsebool -P httpd_can_network_connect on # persistently enable
semanage boolean -l # list all booleans
# SELinux troubleshooting
ausearch -m AVC -ts recent # search audit logs for denials
audit2why < /var/log/audit/audit.log # explain why access was denied
audit2allow -a -M mypolicy # generate policy module
# ---- AppArmor (Ubuntu / SUSE) ----
# Check AppArmor status
sudo aa-status # list profiles and status
sudo apparmor_status # same command
# Profile management
sudo aa-enforce /etc/apparmor.d/usr.sbin.nginx # enforce mode
sudo aa-complain /etc/apparmor.d/usr.sbin.nginx # complain mode (log only)
sudo aa-disable /etc/apparmor.d/usr.sbin.nginx # disable profile
# View profile
sudo cat /etc/apparmor.d/usr.sbin.nginx
# AppArmor log entries
sudo dmesg | grep apparmor
sudo journalctl -k | grep apparmor# sudo — execute as superuser
sudo command # run as root
sudo -u alice command # run as specific user
sudo -i # interactive root shell
sudo -s # shell with current env
sudo -l # list allowed commands
sudo -k # invalidate cached credentials
sudo -v # refresh credentials (extend timeout)
sudo -E command # preserve current environment
# visudo — safely edit sudoers file
sudo visudo # opens /etc/sudoers in $EDITOR
sudo visudo -c # check syntax without editing
sudo visudo -f /etc/sudoers.d/myfile # edit custom sudoers file
# /etc/sudoers syntax rules# User specifications
root ALL=(ALL:ALL) ALL
alice ALL=(ALL) ALL # alice can run any command as anyone
# Group specifications
%wheel ALL=(ALL) ALL # wheel group has full sudo
%devops ALL=(ALL) NOPASSWD: /bin/systemctl, /bin/journalctl
# Specific commands
deployer ALL=(ALL) NOPASSWD: /usr/bin/systemctl restart nginx
deployer ALL=(ALL) /usr/bin/apt update, /usr/bin/apt upgrade
dba ALL=(ALL) /usr/bin/systemctl restart postgresql
# Aliases
User_Alias ADMINS = alice, bob, carol
Cmnd_Alias WEB = /usr/bin/systemctl * nginx, /usr/bin/nginx -t
Host_Alias WEBSERVERS = web1, web2, web3
ADMINS WEBSERVERS=(root) WEB
# Defaults
Defaults timestamp_timeout=15 # credential cache timeout (minutes)
Defaults env_reset # reset environment variables
Defaults secure_path="/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin"
Defaults lecture=always # show lecture every time/etc/sudoers directly — always use sudo visudo. It locks the file and checks syntax before saving. A syntax error in sudoers will lock you out of sudo.Defaults logfile=/var/log/sudo.log in sudoers to log all sudo commands. For production servers, always use specific command grants rather than full ALL=(ALL) ALL.Bash scripting fundamentals for automation, CI/CD pipelines, and system administration tasks.
#!/usr/bin/env bash
set -euo pipefail # strict mode: exit on error, undefined vars, pipe failures
# --- Variables ---
NAME="Alice"
AGE=30
PI=3.14
GREETING="Hello, $NAME" # double quotes: expand variables
LITERAL='Hello, $NAME' # single quotes: literal
PATH_VAR="/home/$USER/bin:$PATH" # string interpolation
# Variable substitution
echo "Length: ${#NAME}" # string length
echo "Upper: ${NAME^^}" # uppercase
echo "Lower: ${NAME,,}" # lowercase
echo "Default: ${UNSET:-default}" # use default if unset/null
echo "Assign: ${UNSET:=default}" # assign default if unset/null
echo "Alt: ${UNSET:+alternate}" # use alternate if set
# Special variables
echo "Script: $0" # script name
echo "Args: $1 $2 $3" # positional arguments
echo "All args: $@" # all args as separate words
echo "All args: $*" # all args as single word
echo "Count: $#" # number of arguments
echo "Last exit: $?" # exit code of last command
echo "PID: $$" # current process ID
echo "Background PID: $!" # last background process PID
# Arrays
FRUITS=("apple" "banana" "cherry")
echo "First: ${FRUITS[0]}"
echo "All: ${FRUITS[@]}"
echo "Count: ${#FRUITS[@]}"
echo "Last: ${FRUITS[-1]}"
FRUITS+=("date") # append element
FRUITS+=("elderberry" "fig") # append multiple
unset FRUITS[1] # remove element
# Associative arrays (Bash 4+)
declare -A SERVERS
SERVERS=( [web]=192.168.1.10 [db]=192.168.1.20 [cache]=192.168.1.30 )
echo "DB: ${SERVERS[db]}"
for key in "${!SERVERS[@]}"; do echo "$key: ${SERVERS[$key]}"; done
# --- Conditionals ---
# if / elif / else
if [[ -f "/etc/nginx/nginx.conf" ]]; then
echo "Nginx config exists"
elif [[ -d "/etc/httpd" ]]; then
echo "Apache config dir exists"
else
echo "No web server config found"
fi
# File test operators
# -f file exists and is regular file
# -d dir exists and is directory
# -e path exists (any type)
# -s file exists and is not empty
# -r file readable
# -w file writable
# -x file executable
# -L file symbolic link
# file1 -nt file2 file1 is newer than file2
# file1 -ot file2 file1 is older than file2
# String tests
if [[ -z "$VAR" ]]; then echo "empty"; fi # zero length
if [[ -n "$VAR" ]]; then echo "not empty"; fi # non-zero length
if [[ "$VAR" == "hello" ]]; then echo "match"; fi # string equality
if [[ "$VAR" != "bye" ]]; then echo "no match"; fi # inequality
if [[ "$VAR" =~ ^[0-9]+$ ]]; then echo "number"; fi # regex match
# Numeric tests (( )) — for arithmetic
if (( AGE >= 18 )); then echo "adult"; fi
if (( $# > 2 )); then echo "too many args"; fi
# Logical operators
if [[ -f "$FILE" && -r "$FILE" ]]; then echo "readable file"; fi
if [[ -d "$DIR" || -d "$ALT_DIR" ]]; then echo "dir exists"; fi#!/usr/bin/env bash
# --- for loop ---
# Iterate over words
for fruit in apple banana cherry; do
echo "Fruit: $fruit"
done
# Iterate over array
for item in "${FRUITS[@]}"; do
echo "Item: $item"
done
# C-style for loop
for ((i = 0; i < 10; i++)); do
echo "Count: $i"
done
# Iterate over files
for file in *.log; do
echo "Processing: $file"
done
# Iterate over command output
for pid in $(pgrep nginx); do
echo "Nginx PID: $pid"
done
# Iterate over lines (safe with IFS)
while IFS= read -r line; do
echo "Line: $line"
done < /etc/passwd
# Iterate with range
for i in {1..10}; do echo "Number: $i"; done
for i in {1..10..2}; do echo "Odd: $i"; done # step 2
# --- while loop ---
count=0
while (( count < 5 )); do
echo "Count: $count"
((count++))
done
# Read from file
while IFS=: read -r user _ uid _ _ home _; do
echo "User: $user, Home: $home, UID: $uid"
done < /etc/passwd
# Read from command
while read -r line; do
echo "$line"
done < <(find . -name "*.py")
# Process substitution
while read -r line; do
echo "$line"
done < <(curl -s https://api.example.com/data)
# --- until loop ---
until ping -c 1 -W 1 google.com &>/dev/null; do
echo "Waiting for network..."
sleep 2
done
echo "Network is up!"
# --- break / continue ---
for i in {1..10}; do
if (( i == 5 )); then continue; fi
if (( i == 8 )); then break; fi
echo "i = $i"
done#!/usr/bin/env bash
# Basic function
greet() {
local name="${1:-World}"
echo "Hello, $name!"
}
greet "Alice" # Hello, Alice!
greet # Hello, World!
# Function with return value (exit code)
is_file() {
if [[ -f "$1" ]]; then
return 0 # success
else
return 1 # failure
fi
}
if is_file "/etc/passwd"; then
echo "File exists"
fi
# Function that outputs to stdout
get_latest_file() {
local dir="${1:-.}"
find "$dir" -maxdepth 1 -type f -printf '%T@ %p\n' | sort -rn | head -1 | cut -d' ' -f2-
}
latest=$(get_latest_file /var/log)
# Recursive function
factorial() {
local n=$1
if (( n <= 1 )); then echo 1; return; fi
local prev=$(factorial $((n - 1)))
echo $((n * prev))
}
echo "5! = $(factorial 5)"
# Function with named parameters (using local)
create_user() {
local username=$1
local shell=${2:-/bin/bash}
local home="/home/$username"
echo "Creating $username with shell $shell at $home"
}
# Use functions from other scripts
# source ./lib.sh or . ./lib.sh#!/usr/bin/env bash
# --- case/esac ---
case "$1" in
start)
echo "Starting service..."
;;
stop)
echo "Stopping service..."
;;
restart)
echo "Restarting service..."
;;
status)
echo "Checking status..."
;;
*)
echo "Usage: $0 {start|stop|restart|status}"
exit 1
;;
esac
# Glob patterns in case
case "$filename" in
*.tar.gz|*.tgz) echo "Tarball" ;;
*.zip) echo "ZIP archive" ;;
*.jpg|*.png|*.gif) echo "Image file" ;;
*) echo "Unknown type" ;;
esac
# --- Here Documents ---
# Write multi-line string to file
cat <<EOF > /etc/myapp/config.yml
server:
host: 0.0.0.0
port: 8080
debug: false
database:
url: postgresql://localhost/mydb
EOF
# Here string (single line)
grep "pattern" <<< "search this string"
# Strip leading tabs (<<-)
cat <<-EOF
Indented content
EOF
# Variable expansion in heredoc (default)
cat <<EOF
Hello, $USER. Today is $(date +%A).
EOF
# Literal heredoc (no expansion)
cat <<'EOF'
This is literal: $USER $(whoami) are not expanded.
EOF
# --- trap — signal handling ---
cleanup() {
echo "Cleaning up..."
rm -f /tmp/myapp_$$
exit 0
}
trap cleanup EXIT # run on script exit
trap cleanup INT TERM # run on Ctrl+C and SIGTERM
trap 'echo "Ctrl+C pressed"; exit 130' INT
trap 'echo "Ignoring USR1"; ' USR1
# --- getopts — parse command-line options ---
usage() { echo "Usage: $0 [-v] [-f file] [-o output] args"; exit 1; }
verbose=0
file=""
output=""
while getopts ":vf:o:" opt; do
case $opt in
v) verbose=1 ;;
f) file="$OPTARG" ;;
o) output="$OPTARG" ;;
\?) echo "Invalid option: -$OPTARG" >&2; usage ;;
:) echo "Option -$OPTARG requires argument." >&2; usage ;;
esac
done
shift $((OPTIND - 1)) # remove parsed options from $@
# --- read — read input ---
echo "Enter your name: "
read -r name # -r: don't interpret backslashes
read -r -p "Password: " -s pass # -s: silent (no echo)
read -r -t 10 -p "Timeout: " answer # -t: timeout (seconds)
read -r -n 1 -p "Continue? [y/N] " confirm # -n: max chars
# Read into array
echo "1 2 3 4 5" | read -ra numbers
echo "${numbers[@]}" # 1 2 3 4 5
# Read from file line by line
while IFS= read -r line; do
echo "$line"
done < input.txt
# --- printf — formatted output ---
printf "Hello, %s! You are %d years old.\n" "Alice" 30
printf "%-20s %-10s %8s\n" "Name" "Status" "Size"
printf "%-20s %-10s %8d\n" "file1.txt" "OK" 1024
printf "%05d\n" 42 # 00042
printf "%x\n" 255 # ff (hex)
printf "%o\n" 8 # 10 (octal)
printf "%.2f\n" 3.14159 # 3.14#!/usr/bin/env bash
set -euo pipefail
# --- Exit codes ---
# 0 = success, 1 = general error, 2 = misuse, 127 = command not found
# 130 = Ctrl+C (SIGINT), 137 = SIGKILL (128 + 9)
exit 0 # success
exit 1 # failure
exit 2 # usage error
# Use exit codes in conditionals
if grep -q "error" /var/log/app.log; then
echo "Errors found"
exit 1
fi
# Chained commands with exit codes
command1 && command2 # run command2 only if command1 succeeds
command1 || command2 # run command2 only if command1 fails
command1 && command2 || command3 # if/then/else pattern
# --- Sourcing ---
# source (or .) executes script in current shell (shares variables)
source ./lib.sh
. ./config.sh
source ~/.bashrc
# After sourcing, functions and variables from the file are available
# This is how ~/.bashrc, ~/.bash_profile work
# --- Subshells ---
# ( ) runs in a subshell (separate process, copies variables)
(
cd /tmp
echo "Inside subshell: $(pwd)"
VAR="subshell value"
)
echo "Outside subshell: $(pwd)" # unchanged
echo "VAR: $VAR" # unchanged (was set in subshell)
# Subshells are useful for:
# - cd without affecting parent
# - temporary variable changes
# - parallel processing (background subshells)
for dir in /var/log/*; do
(cd "$dir" && echo "$PWD: $(ls -1 | wc -l) files") &
done
wait # wait for all background subshells| Set Option | Effect | Description |
|---|---|---|
| set -e | Exit on error | Script exits immediately if any command returns non-zero |
| set -u | Unset variables | Treat unset variables as errors |
| set -o pipefail | Pipe failure | Pipeline fails if ANY command in it fails |
| set -x | Debug trace | Print each command before executing |
| set -v | Verbose | Print shell input lines as they are read |
| set -f | No glob | Disable pathname expansion (globbing) |
| set -a | Auto export | All variables are automatically exported |
| set -n | Dry run | Read commands but don't execute (syntax check) |
#!/usr/bin/env bash and set -euo pipefail. The env approach is more portable than #!/bin/bash since bash may be in different locations on different systems.The Unix philosophy: combine small, powerful tools to process text data. Master these and you can transform any text stream.
# Basic grep
grep "pattern" file.txt # search in file
grep "error" /var/log/syslog # search system log
grep -i "ERROR" log.txt # case-insensitive
grep -v "exclude" file.txt # invert match (non-matching)
grep -r "function" ./src/ # recursive search
grep -rl "TODO" ./ # list filenames only (with match)
grep -Rn "import" ./src/ # recursive + line numbers
grep -c "error" access.log # count matches
grep -n "pattern" file.txt # show line numbers
grep -w "word" file.txt # whole word match
grep -x "^exact line$" file.txt # exact line match
grep -m 5 "error" log.txt # stop after 5 matches
# Extended regex (-E or egrep)
grep -E "error|warning|critical" log.txt # alternation (OR)
grep -E "^[0-9]{1,3}\.[0-9]{1,3}" file # IP address pattern
grep -E "^(root|www-data):" /etc/passwd # anchored alternation
grep -E "\b[A-Z][a-z]+\b" file.txt # word boundary matching
# Context lines
grep -B 2 "error" log.txt # 2 lines before match
grep -A 2 "error" log.txt # 2 lines after match
grep -C 3 "error" log.txt # 3 lines before and after
# Color and output
grep --color=always "pattern" file
grep -o "pattern" file.txt # only matching part
grep -H "pattern" *.txt # show filename
grep -h "pattern" *.txt # hide filename
grep --include="*.py" -r "import" .
# Search streams
cat access.log | grep "404"
ps aux | grep -v grep | grep nginx
dmesg | grep -i "error|fail"
# Fixed string (no regex)
grep -F "file.name" file.txt # literal match (faster for fixed strings)
grep -rf "search/" ./ # recursive with fixed strings# sed syntax: sed [options] 'command' [file]
# --- Substitution (s) ---
sed 's/old/new/' file.txt # replace first occurrence per line
sed 's/old/new/g' file.txt # replace all occurrences
sed 's/old/new/2' file.txt # replace 2nd occurrence
sed 's/old/new/2g' file.txt # replace from 2nd occurrence onward
sed -i 's/old/new/g' file.txt # edit file in-place
sed -i.bak 's/old/new/g' file.txt # in-place with backup
# Delimiter alternatives (when / is in pattern)
sed 's|/usr/local|/opt|g' file.txt
sed 's#/path/to/old#/path/to/new#g' file.txt
# Case-insensitive
sed 's/hello/world/gI' file.txt
# --- Delete lines (d) ---
sed '/^#/d' file.txt # delete comment lines
sed '/^$/d' file.txt # delete blank lines
sed '/^#/d; /^$/d' file.txt # delete comments AND blanks
sed '1,5d' file.txt # delete lines 1-5
sed '10,$d' file.txt # delete from line 10 to end
# --- Print (p) ---
sed -n '/pattern/p' file.txt # print matching lines only
sed -n '10,20p' file.txt # print lines 10-20
sed -n '1p;$p' file.txt # print first and last line
# --- Replace entire line ---
sed '/^GRUB_TIMEOUT=/c\GRUB_TIMEOUT=0' /etc/default/grub
# --- Append / Insert / Change ---
sed '1i\# Header line' file.txt # insert before line 1
sed '1a\# Footer line' file.txt # append after line 1
sed '3c\new content' file.txt # replace line 3
# --- Address ranges ---
sed -n '/start/,/end/p' file.txt # print between markers
sed '10,20s/old/new/g' file.txt # substitute in lines 10-20
# --- Useful one-liners ---
sed 's/\r$//' file.txt # remove Windows carriage returns
sed 's/[ \t]*$//' file.txt # remove trailing whitespace
sed 's/^ *//' file.txt # remove leading spaces
sed '/./,$!d' file.txt # remove leading blank lines
sed '1s/^/\n/' file.txt # add blank line at top
# --- Multiple commands ---
sed -e 's/old/new/g' -e 's/foo/bar/g' file.txt
sed '
s/old/new/g
/pattern/d
s/foo/bar/g
' file.txt# awk syntax: awk 'BEGIN{...} /pattern/{...} END{...}' file
# Built-in variables: NR, NF, $0, $1..$n, FS, OFS, FILENAME, FNR
# Print columns
awk '{print $1}' file.txt # first column
awk '{print $1, $3}' file.txt # columns 1 and 3
awk '{print $NF}' file.txt # last column
awk '{print $(NF-1)}' file.txt # second-to-last column
awk '{print NR, $0}' file.txt # line number + entire line
# Change field separator
awk -F: '{print $1}' /etc/passwd # colon-separated
awk -F',' '{print $2, $5}' data.csv # comma-separated
awk 'BEGIN{FS=":"; OFS=" - "}{print $1, $7}' /etc/passwd
# Pattern matching
awk '/error/' file.txt # lines containing "error"
awk '$3 > 100' file.txt # where field 3 > 100
awk '$1 == "GET"' access.log # exact match
awk '/start/,/end/' file.txt # range pattern
awk '!/exclude/' file.txt # lines NOT containing pattern
# BEGIN/END blocks
awk 'BEGIN{sum=0} {sum+=$1} END{print "Total:", sum}' numbers.txt
awk 'BEGIN{max=0} {if($1>max)max=$1} END{print "Max:", max}' data.txt
# Built-in functions
awk '{print length($0)}' file.txt # line length
awk '{print toupper($1)}' file.txt # uppercase
awk '{print tolower($0)}' file.txt # lowercase
awk '{printf "%-15s %8d\n", $1, $3}' file.txt # formatted print
awk '{gsub(/old/, "new", $0); print}' file.txt # global substitution
awk '{print substr($0, 1, 10)}' file.txt # substring
# Conditional logic
awk '{
if ($3 > 90) grade = "A";
else if ($3 > 80) grade = "B";
else grade = "C";
print $1, grade
}' grades.txt
# Filtering and aggregation
awk '$7 >= 400 {total++; sum+=$7} END{print "Count:", total, "Avg:", sum/total}' data.txt
# Associative arrays
awk '{count[$1]++} END{for (key in count) print key, count[key]}' access.log
# Example: parse Apache access log
awk '{
split($4, dt, "[/:]")
month=dt[2]; day=dt[3]; hour=dt[4]
ip=$1; status=$9; size=$10
requests[ip]++
bytes[ip] += size
}
END {
for (ip in requests)
printf "%-18s %6d reqs %10.2f MB\n", ip, requests[ip], bytes[ip]/1048576
}' /var/log/apache2/access.log | sort -k3 -rn | head -10# cut — extract columns/fields
cut -d: -f1 /etc/passwd # field 1 (colon delimiter)
cut -d: -f1,7 /etc/passwd # fields 1 and 7
cut -d: -f1-5 /etc/passwd # fields 1 through 5
cut -c1-10 file.txt # characters 1-10
cut -c1,5,10 file.txt # specific character positions
cut -d' ' -f2- file.txt # fields 2+ (space delimiter)
ps aux | tr -s ' ' | cut -d' ' -f2,11 # PID and command
# paste — merge lines from files
paste file1.txt file2.txt # side by side
paste -d',' file1.txt file2.txt # comma-separated
paste -s file.txt # paste lines into one line
# tr — translate or delete characters
echo "HELLO" | tr 'A-Z' 'a-z' # lowercase
echo "hello" | tr 'a-z' 'A-Z' # uppercase
echo "hello 123" | tr -d '0-9' # delete digits
echo "hello world" | tr -s ' ' # squeeze consecutive spaces
echo "Hello World" | tr ' ' '\n' # space to newline
cat file.txt | tr -d '\r' # remove carriage returns
echo "{hello}" | tr -d '{}' # delete specific characters
echo "hello" | tr '[:lower:]' '[:upper:]' # POSIX character classes
# join — join lines on common field (like SQL JOIN)
# Both files must be sorted on the join field
join -t: -1 1 -2 1 users.txt orders.txt # inner join
join -a1 -t: -1 1 -2 1 users.txt orders.txt # left outer join
join -a2 -t: -1 1 -2 1 users.txt orders.txt # right outer join
join -e "NULL" -a1 -a2 file1 file2 # full outer join
join -o 1.1,2.2 -t: file1 file2 # select specific fields
# column — format text into columns
cat /etc/passwd | column -t -s: # pretty-print passwd
mount | column -t # format mount output
echo -e "Name\tAge\tCity\nAlice\t30\tNYC\nBob\t25\tLA" | column -t -s$'\t'# --- jq — JSON processor ---
# Read JSON
cat data.json | jq '.'
cat data.json | jq '.name'
cat data.json | jq '.items[0]'
cat data.json | jq '.items[] | .name' # all item names
cat data.json | jq '.items[] | select(.age > 25)'
cat data.json | jq '.items | length'
cat data.json | jq '.items[] | .name, .price'
cat data.json | jq 'keys'
cat data.json | jq 'has("email")'
cat data.json | jq '. as $item | {name: $item.name, upper: ($item.name | uppercase)}'
# Extract specific fields
curl -s https://api.github.com/users/octocat | jq '{login, name, public_repos}'
curl -s https://api.example.com/data | jq -r '.token' # raw output
# Array operations
echo '[1,2,3,4,5]' | jq 'map(select(. > 3))' # [4,5]
echo '[1,2,3]' | jq 'add' # 6
echo '[[1,2],[3,4]]' | jq 'flatten' # [1,2,3,4]
echo '[3,1,2]' | jq 'sort' # [1,2,3]
echo '[1,1,2,3]' | jq 'unique' # [1,2,3]
echo '[{a:1},{a:2}]' | jq 'group_by(.a) | map(.[0].a)' # [1,2]
# Format output
cat data.json | jq -C '.' # color output
cat data.json | jq '.' | less -R # paged colored output
cat data.json | jq -r '.[] | "\(.name): \(.value)"' # custom format
cat data.json | jq 'to_entries | from_entries' # transform object
# --- xmllint — XML processor ---
xmllint --format data.xml # pretty-print XML
xmllint --xpath '//item/name' data.xml # XPath query
xmllint --xpath 'string(//item[1]/@id)' data.xml # attribute value
xmllint --shell data.xml # interactive shell
xmllint --valid data.xml # validate against DTD
xmllint --schema schema.xsd data.xml # validate against XSD
xmllint --xpath '//book[price>35]/title' data.xml # conditional XPath
# --- Regular Expressions ---
# BRE (Basic): grep, sed (default)
# ERE (Extended): grep -E, sed -E, awk
# Character classes
[abc] # any of a, b, c
[a-z] # lowercase letters
[A-Za-z] # any letter
[0-9] # digits
[^abc] # not a, b, or c
. # any character (except newline)
\d # digit (ERE with -P / PCRE)
\w # word character [a-zA-Z0-9_]
\s # whitespace
# Quantifiers
* # 0 or more
\+ # 1 or more (ERE)
? # 0 or 1
{n} # exactly n times
{n,} # at least n
{n,m} # between n and m
# Anchors
^ # start of line
$ # end of line
\b # word boundary
# Groups
\(\) # group (BRE), \1 for backreference
( ) # group (ERE), $1 for backreference
| # alternation (ERE)
# Examples
grep -E '^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}$' file # IPv4
grep -E '^[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,}$' file # Email
grep -P '^\s*//' file # Line comments
grep -E '^(feat|fix|chore|docs|refactor)(\(.+\))?: ' CHANGELOG # Conventional commits# Top 10 most requested IPs from Apache log
awk '{print $1}' /var/log/apache2/access.log | sort | uniq -c | sort -rn | head -10
# Find largest files
du -ah / | sort -rh | head -20
# Count file types
find . -type f | sed 's/.*\.//' | sort | uniq -c | sort -rn | head
# Monitor 404 errors in real-time
tail -f /var/log/nginx/access.log | grep --color " 404 "
# Extract all URLs from HTML
grep -oP 'href="\K[^"]+' index.html | sort -u
# Convert CSV to TSV
cat data.csv | tr ',' '\t' > data.tsv
# Word frequency analysis
cat file.txt | tr '[:space:]' '\n' | tr '[:upper:]' '[:lower:]' \
| grep -v '^$' | sort | uniq -c | sort -rn | head -20
# Disk usage by file type
find / -type f -printf '%s %s\n' | awk '{ext=substr($2, match($2, /\.[^.]+$/)) \
; size[ext]+=$1; count[ext]++} END {for (e in size) \
printf "%10.2f MB %5d files %s\n", size[e]/1048576, count[e], e}' \
| sort -rn | head -15| Tool | Best For | Key Flag |
|---|---|---|
| grep | Pattern matching / filtering | -E (extended regex), -r (recursive) |
| sed | Stream editing / substitution | -i (in-place), -n (quiet) |
| awk | Column processing / reporting | -F (field sep), BEGIN/END blocks |
| cut | Extract fields/columns | -d (delimiter), -f (fields) |
| tr | Character translation | -d (delete), -s (squeeze) |
| sort | Sorting lines | -n (numeric), -k (key), -h (human) |
| uniq | Deduplication | -c (count), -d (duplicates), -u (unique) |
| jq | JSON processing | .field, select(), map(), pipes |
| column | Column alignment | -t (table), -s (separator) |
| paste | Merging columns | -d (delimiter), -s (serial) |
grep, awk, sed, sort, and uniq and you can answer almost any data question directly from the command line.Common Linux interview questions with detailed answers and command examples.
A process is an independent execution environment with its own memory space (code, data, heap, stack), file descriptors, and process ID. A thread (lightweight process) shares memory space, file descriptors, and other resources with other threads in the same process but has its own stack and registers.
| Property | Process | Thread |
|---|---|---|
| Memory | Separate address space | Shared address space |
| Creation | fork() — slow (copies pages) | pthread_create() — fast |
| Context switch | Expensive | Inexpensive |
| Communication | IPC (pipes, sockets, shared mem) | Shared memory directly |
| Failure | Isolated (one crash = that process) | Shared (one crash = all threads) |
| Resource ownership | Owns resources | Shares parent process resources |
BIOS/UEFI → Power-on self-test (POST), finds boot device → Bootloader (GRUB2) loads kernel into memory → Kernel initializes hardware, sets up memory management, mounts root filesystem → initramfs (initial RAM filesystem) provides early userspace drivers/modules → init process (systemd on modern Linux) starts as PID 1, reads targets → systemd activates default target (graphical.target or multi-user.target), starts services, mounts filesystems → Login prompt appears.
# View boot process details
dmesg # kernel ring buffer messages
journalctl -b # current boot logs
journalctl -b -1 # previous boot logs
systemd-analyze # boot time breakdown
systemd-analyze blame # time per service
systemd-analyze critical-chain # dependency chainA hard link is an additional directory entry pointing to the same inode as the original file. Both names refer to the same data on disk. The data is only deleted when all hard links are removed (link count reaches 0). Hard links cannot cross filesystems or point to directories.
A soft (symbolic) link is a special file containing a path string pointing to another file. It has its own inode. If the target is deleted, the symlink becomes a dangling/broken link. Symlinks can cross filesystems and point to directories.
ln target hardlink # hard link
ln -s target symlink # symbolic link
ls -li # inode numbers reveal linksLinux uses virtual memory — each process gets its own virtual address space. The kernel maps virtual pages to physical pages via page tables. Demand paging loads pages only when accessed (page fault). Copy-on-write (COW) shares pages between forked processes until one writes. SWAP moves inactive pages to disk when physical RAM is full. OOM Killer terminates processes when memory is critically low (controlled by /proc/<pid>/oom_score_adj). Page cache caches file data in unused RAM.
# Memory diagnostics
free -h # memory usage summary
cat /proc/meminfo # detailed memory info
vmstat 1 # virtual memory stats (1s interval)
top -o %MEM # processes sorted by memory
slabtop # kernel slab cache allocator
cat /proc/sys/vm/swappiness # swap tendency (0-100)
cat /proc/<pid>/status | grep Vm # process memory breakdownA zombie process has completed execution but its entry remains in the process table because its parent hasn't called wait() to read the exit status. Zombies use no CPU or memory (just a process table entry), but too many can exhaust the PID limit.
Solutions: Fix the parent process to call waitpid(). Kill the parent process (orphans are adopted by init/systemd, which auto-reaps them). Use prctl(PR_SET_PDEATHSIG, SIGKILL) in child processes.
# Find zombie processes
ps aux | awk '$8 ~ /Z/ {print $2, $11}' # STAT column = Z
ps -eo pid,ppid,stat,cmd | grep Z
# Count zombies
ps aux | awk '{if ($8 == "Z") count++} END {print "Zombies:", count+0}'
echo $(ps aux | awk '$8 ~ /Z/' | wc -l) zombie processes
# Fix: kill parent to make init reap zombies
ps -eo ppid,pid,stat,cmd | awk '$3 ~ /Z/ {print $1}' | sort -u | xargs killSIGTERM (15) is a polite request to terminate. The process can catch this signal, perform cleanup (close files, save state, release locks), and exit gracefully. This is the default signal sent by kill.
SIGKILL (9) immediately terminates the process at the kernel level. It cannot be caught, blocked, or ignored. The process gets no chance to clean up. Use only when SIGTERM fails.
# Graceful shutdown sequence
kill -TERM 1234 # ask nicely first
sleep 5 # give it time to clean up
if kill -0 1234 2>/dev/null; then # still running?
kill -KILL 1234 # force kill
fi
# Common signal use cases
kill -HUP <pid> # reload configuration (many daemons)
kill -USR1 <pid> # custom signal (application-defined)
kill -STOP <pid> # pause process (SIGSTOP)
kill -CONT <pid> # resume paused process/proc is a virtual filesystem (procfs) that provides a window into the kernel and running processes. It doesn't exist on disk — it's generated on-the-fly by the kernel. Each process has a directory at /proc/<pid>/ containing its information (cmdline, status, environ, fd, maps, limits). System-wide info is in files like /proc/cpuinfo, /proc/meminfo, /proc/loadavg, and /proc/sys/ (kernel parameters). The /proc/sys/ directory is writable (with root), allowing runtime kernel tuning via sysctl.
# Useful /proc files
cat /proc/cpuinfo # CPU model, cores, cache
cat /proc/meminfo # RAM, swap, page tables
cat /proc/loadavg # 1, 5, 15 min load averages
cat /proc/uptime # seconds since boot
cat /proc/version # kernel version + gcc
cat /proc/partitions # block device partitions
cat /proc/interrupts # IRQ assignments
cat /proc/sys/net/ipv4/ip_forward # IP forwarding status
# Tune kernel parameters
sysctl net.core.somaxconn=1024
sysctl -w vm.swappiness=10
echo 1 > /proc/sys/net/ipv4/ip_forwardStep-by-step approach:
# 1. Identify the process consuming CPU
top -o %CPU # or htop
ps aux --sort=-%cpu | head -10
# 2. Check what the process is doing
strace -p <pid> # system call trace
strace -p <pid> -c # summary of syscalls
lsof -p <pid> # open files and connections
cat /proc/<pid>/status # process status
cat /proc/<pid>/wchan # what kernel function it's waiting in
# 3. Check threads within the process
top -H -p <pid> # per-thread CPU
ps -T -p <pid> # list all threads
# 4. Profile the application
perf top -p <pid> # live profiling
perf record -g -p <pid> -- sleep 30 # record for 30 seconds
perf report # analyze recording
# 5. Check for runaway child processes
pstree -p <pid> # process tree
pgrep -P <pid> # child processes
# 6. If it's a web app, check connection overload
ss -s # socket statistics
netstat -an | grep :80 | wc -l # number of connections
ab -n 1000 -c 100 URL # stress test locallyLoad average (uptime or cat /proc/loadavg) shows three numbers: 1-minute, 5-minute, and 15-minute averages of processes in the runnable or uninterruptible sleep (D state) states.
A load of 1.0means one CPU core is fully utilized. On a 4-core machine, load > 4 means all cores are at capacity. If load is much higher than the number of CPU cores, processes are queuing. The 3 numbers help identify trends: if 1-min >> 5-min >> 15-min, load is increasing. If 1-min << 5-min << 15-min, load is decreasing.
# Check load average
uptime # users, load averages, uptime
cat /proc/loadavg # 0.5 0.3 0.2 2/142 12345
# Fields: 1min 5min 15min running/total lastpid
nproc # number of CPU cores
lscpu | grep "^CPU(s):" # core countSELinux (Security-Enhanced Linux) is a Mandatory Access Control (MAC) system built into the Linux kernel. It goes beyond standard Unix DAC (Discretionary Access Control) by enforcing a security policy defined by the administrator.
Standard permissions let the owner decide access (DAC). SELinux adds a policy layer where the kernel decides — even root is subject to SELinux rules. Every process and file has an SELinux context (user:role:type). Policies define which process types can access which file types.
Modes: Enforcing (blocks violations), Permissive (logs but allows), Disabled (off). When a web server is compromised, SELinux prevents it from reading /etc/shadow even if running as root — because the httpd_t domain type cannot access shadow_t files.
# SELinux diagnostics
getenforce # current mode
sestatus # full status
ls -Z /var/www/html/ # file contexts
ps -Z # process contexts
ausearch -m AVC -ts recent # denied actions
audit2why < /var/log/audit/audit.log # explain denials