■
UNIX / LINUX
Shell Scripting
Complete Reference Guide
Commands • Examples • Shell Scripting • Data Types
Control Flow • Functions • Arrays • File I/O • Advanced Topics
Comprehensive study notes for Unix & Shell Scripting
Table of Contents
1. Unix Fundamentals — The Shell & File System
2. File & Directory Commands
3. File Permissions & Ownership
4. Process Management
5. Text Processing Commands
6. Searching & Filtering
7. Networking Commands
8. Archiving & Compression
9. Disk & System Monitoring
10. Redirection, Pipes & Special Characters
11. Shell Scripting — Introduction
12. Variables & Data Types
13. Operators
14. Control Flow — if / elif / else
15. Loops — for, while, until
16. case Statement
17. Functions
18. Arrays
19. String Manipulation
20. File I/O in Scripts
21. Error Handling & Debugging
22. Regular Expressions
23. Advanced Topics & Best Practices
Unix & Shell Scripting — Complete Reference Page 2
1. Unix Fundamentals — The Shell & File
System
Unix is a multiuser, multitasking operating system. The shell is a command-line interpreter that acts as the
interface between the user and the kernel. Common shells include bash (Bourne Again Shell), sh, zsh, ksh,
and csh.
Unix File System Hierarchy
Command / Syntax Description & Notes
/ Root directory — top of the file system tree
/home Home directories for regular users
/root Home directory for the root (superuser)
/bin Essential user binaries (ls, cp, mv …)
/usr/bin Non-essential user binaries
/sbin System binaries (used by root)
/etc System-wide configuration files
/var Variable data: logs, spools, tmp files
/tmp Temporary files (cleared on reboot)
/dev Device files (disks, terminals …)
/proc Virtual filesystem — kernel & process info
/lib Shared libraries needed by /bin & /sbin
/mnt Mount point for temporary filesystems
/opt Optional/third-party software packages
Getting Help
Command / Syntax Description & Notes
man ls Open the manual page for the ls command
ls --help Quick help / usage summary
info bash GNU info documentation for bash
whatis ls One-line description of command
whereis ls Locate binary, source, and man pages
which python3 Show full path of command in PATH
type cd Show command type (builtin, alias, file …)
Unix & Shell Scripting — Complete Reference Page 3
2. File & Directory Commands
Navigation
Command / Syntax Description & Notes
pwd Print Working Directory — show current path
cd /path/to/dir Change to absolute path
cd .. Go up one directory level
cd ~ Go to home directory
cd - Toggle back to previous directory
ls List files in current directory
ls -l Long listing with permissions, size, date
ls -la Long listing including hidden files (dot files)
ls -lh Human-readable file sizes (KB, MB …)
ls -lt Sort by modification time, newest first
ls -R List recursively through subdirectories
ls -lS Sort by file size, largest first
tree Display directory structure as a tree
Creating, Copying, Moving & Deleting
Command / Syntax Description & Notes
touch [Link] Create empty file or update timestamp
mkdir mydir Create a new directory
mkdir -p a/b/c Create nested directories (no error if exists)
cp [Link] [Link] Copy file to destination
cp -r srcdir/ dstdir/ Copy directory recursively
cp -p file1 file2 Copy preserving permissions/timestamps
mv [Link] [Link] Rename file (or move to another location)
mv [Link] /tmp/ Move file to /tmp directory
rm [Link] Remove (delete) a file
rm -r mydir/ Remove directory and all contents recursively
rm -rf mydir/ Force remove without prompts — USE WITH CARE
rmdir emptydir/ Remove empty directory only
ln -s target linkname Create a symbolic (soft) link
ln target hardlink Create a hard link
Unix & Shell Scripting — Complete Reference Page 4
Viewing File Contents
Command / Syntax Description & Notes
cat [Link] Print entire file to stdout
cat -n [Link] Print file with line numbers
less [Link] Scroll through file (q to quit, / to search)
more [Link] Page through file (older, less features)
head [Link] Show first 10 lines
head -n 20 [Link] Show first 20 lines
tail [Link] Show last 10 lines
tail -n 30 [Link] Show last 30 lines
tail -f logfile Follow file — stream new lines as appended
wc [Link] Count lines, words, characters
wc -l [Link] Count lines only
wc -w [Link] Count words only
file [Link] Identify file type
stat [Link] Detailed file metadata (size, inode, times …)
Practical Examples
• Backup a config file: cp /etc/nginx/[Link] /etc/nginx/[Link]
• Find big files quickly: ls -lhS /var/log/ | head -20
• Watch a growing log: tail -f /var/log/syslog
• Count lines in a file: wc -l /etc/passwd → shows number of user accounts
Unix & Shell Scripting — Complete Reference Page 5
3. File Permissions & Ownership
Every file has three permission sets: owner (u), group (g), others (o). Each set has three bits: r (read=4), w
(write=2), x (execute=1).
-rwxr-xr-- 1 alice staff 4096 Jan 1 10:00 [Link]
• - = regular file (d = dir, l = symlink)
• rwx = owner has read+write+execute
• r-x = group has read+execute, no write
• r-- = others have read only
Command / Syntax Description & Notes
chmod 755 [Link] rwxr-xr-x — owner all, group/others read+exec
chmod 644 [Link] rw-r--r-- — owner read/write, others read
chmod 600 [Link] rw------- — owner read/write only (private key)
chmod +x [Link] Add execute permission for all
chmod u+x [Link] Add execute for owner only
chmod g-w [Link] Remove write from group
chmod o-rx [Link] Remove read+exec from others
chmod -R 755 mydir/ Apply permissions recursively
chown alice [Link] Change owner to alice
chown alice:staff [Link] Change owner and group
chown -R alice mydir/ Recursive ownership change
chgrp developers [Link] Change group only
umask Show default permission mask
umask 022 Set umask — new files get 644, dirs get 755
Special Permissions
Command / Syntax Description & Notes
chmod u+s exec SetUID — runs as file owner, not caller
chmod g+s dir/ SetGID — new files inherit directory group
chmod +t /tmp Sticky bit — only owner can delete their files
ls -l /tmp Notice 't' at end of permissions: drwxrwxrwt
sudo command Run command as root (superuser)
su - alice Switch to user alice (full login shell)
visudo Safely edit /etc/sudoers file
■ Octal quick-reference: 7=rwx 6=rw- 5=r-x 4=r-- 0=---
Unix & Shell Scripting — Complete Reference Page 6
4. Process Management
Command / Syntax Description & Notes
ps Snapshot of current user's processes
ps aux All processes, full detail (BSD style)
ps -ef All processes, full detail (System V style)
ps aux | grep nginx Find process by name
top Interactive process monitor (press q to quit)
htop Enhanced interactive process monitor
pgrep nginx Find PID by process name
kill PID Send SIGTERM (graceful stop) to process
kill -9 PID Send SIGKILL (force kill) to process
kill -l List all signal names
killall nginx Kill all processes named nginx
pkill -f pattern Kill processes matching pattern
jobs List background/stopped jobs in current shell
bg %1 Resume job 1 in background
fg %1 Bring job 1 to foreground
command & Run command in background
nohup cmd & Run command immune to hangup (survives logout)
nice -n 10 cmd Run with lower CPU priority (niceness 10)
renice -n 5 -p PID Change priority of running process
wait Wait for all background jobs to finish
sleep 5 Pause shell for 5 seconds
time command Measure execution time of command
Process States
Command / Syntax Description & Notes
R Running or runnable
S Interruptible sleep (waiting for event)
D Uninterruptible sleep (usually I/O)
Z Zombie — finished but not yet reaped by parent
T Stopped (e.g., by Ctrl+Z)
Signals Quick Reference
Command / Syntax Description & Notes
Unix & Shell Scripting — Complete Reference Page 7
SIGHUP (1) Hang up — reload config (used by daemons)
SIGINT (2) Interrupt — same as Ctrl+C
SIGKILL (9) Kill — cannot be caught or ignored
SIGTERM (15) Terminate — default, can be caught
SIGSTOP (19) Stop — cannot be caught; pauses process
SIGCONT (18) Continue — resumes a stopped process
Unix & Shell Scripting — Complete Reference Page 8
5. Text Processing Commands
echo & printf
Command / Syntax Description & Notes
echo "Hello World" Print text with trailing newline
echo -n "no newline" Print without trailing newline
echo -e "line1\nline2" Interpret escape sequences
printf "%-10s %5d\n" a 42 Formatted output (like C printf)
sort, uniq, cut, paste
Command / Syntax Description & Notes
sort [Link] Sort lines alphabetically
sort -n [Link] Sort numerically
sort -r [Link] Sort in reverse order
sort -k2 [Link] Sort by second field (column)
sort -u [Link] Sort and remove duplicates
uniq [Link] Remove consecutive duplicate lines
uniq -c [Link] Count occurrences of each line
uniq -d [Link] Show only duplicated lines
cut -d: -f1 /etc/passwd Cut field 1 using : as delimiter
cut -c1-5 [Link] Cut characters 1 through 5
paste file1 file2 Merge files side by side
tr — Translate Characters
Command / Syntax Description & Notes
tr 'a-z' 'A-Z' < file Convert lowercase to uppercase
tr -d '\n' < file Delete all newlines
tr -s ' ' < file Squeeze multiple spaces to one
tr -d '[:digit:]' < file Remove all digit characters
awk — Pattern Scanning & Reporting
Command / Syntax Description & Notes
awk '{print $1}' file Print first field of each line
awk '{print $1, $3}' file Print fields 1 and 3
awk -F: '{print $1}' /etc/passwd Use : as field separator
awk 'NR==3' file Print line number 3 only
Unix & Shell Scripting — Complete Reference Page 9
awk 'NR>=2 && NR<=5' file Print lines 2 to 5
awk '{sum+=$1} END{print sum}' Sum first column
awk '/pattern/{print}' file Print lines matching pattern
awk '{print NR, $0}' file Prefix each line with line number
awk 'END{print NR}' file Count total lines
sed — Stream Editor
Command / Syntax Description & Notes
sed 's/old/new/' file Replace first occurrence per line
sed 's/old/new/g' file Replace all occurrences (global)
sed -n '5p' file Print line 5 only (-n suppresses default)
sed '2,4d' file Delete lines 2 through 4
sed '/pattern/d' file Delete lines matching pattern
sed -i 's/foo/bar/g' file Edit file IN PLACE (modifies file)
sed '1s/^/header\n/' file Insert a header line
sed -n '/start/,/end/p' Print block between start and end patterns
■ Always test sed -i on a copy first. Use sed -[Link] to create backup automatically.
Unix & Shell Scripting — Complete Reference Page 10
6. Searching & Filtering
grep — Search Text
Command / Syntax Description & Notes
grep 'pattern' file Search for pattern in file
grep -i 'pattern' file Case-insensitive search
grep -r 'pattern' dir/ Recursive search in directory
grep -n 'pattern' file Show matching line numbers
grep -v 'pattern' file Invert match — lines NOT matching
grep -c 'pattern' file Count matching lines
grep -l 'pattern' *.txt List files containing pattern
grep -w 'word' file Match whole word only
grep -A3 'pattern' file Show 3 lines After match
grep -B3 'pattern' file Show 3 lines Before match
grep -E 'pat1|pat2' file Extended regex — match pat1 OR pat2
grep -P '\d+' file Perl-compatible regex (PCRE)
grep -o 'pattern' file Print only the matching part
find — Search for Files
Command / Syntax Description & Notes
find . -name '[Link]' Find by exact name from current dir
find . -name '*.py' Find all Python files
find . -iname '*.PY' Case-insensitive name match
find /home -type f Find regular files only
find /home -type d Find directories only
find . -size +10M Files larger than 10 MB
find . -size -1k Files smaller than 1 KB
find . -mtime -7 Modified within last 7 days
find . -mtime +30 Modified more than 30 days ago
find . -user alice Files owned by alice
find . -perm 644 Files with exact permissions 644
find . -name '*.log' -delete Find and delete matching files
find . -name '*.sh' -exec chmod +x {} \; Find & run command on each
find . -empty Find empty files and directories
find . -maxdepth 2 -name '*.c' Search at most 2 levels deep
Unix & Shell Scripting — Complete Reference Page 11
locate & updatedb
Command / Syntax Description & Notes
locate filename Fast search using database index
updatedb Update the locate database (run as root)
locate -i filename Case-insensitive locate
■ locate uses a pre-built database and is faster than find, but may be out of date. Run updatedb to refresh it.
Unix & Shell Scripting — Complete Reference Page 12
7. Networking Commands
Command / Syntax Description & Notes
ping [Link] Test network connectivity (Ctrl+C to stop)
ping -c 4 [Link] Send exactly 4 packets
traceroute [Link] Show packet route to host
nslookup [Link] DNS lookup — find IP of hostname
dig [Link] Detailed DNS query
dig @[Link] [Link] A Query specific DNS server for A record
host [Link] Simple DNS lookup
ip addr Show IP addresses and interfaces (modern)
ip route Show routing table
ifconfig Show network interfaces (older systems)
netstat -tuln List open ports and listening services
ss -tuln Modern replacement for netstat
curl [Link] Fetch URL content
curl -O [Link] Download file keeping original name
curl -L -o [Link] url Follow redirects, save as [Link]
curl -I url Fetch HTTP headers only
wget [Link] Download file (resumes with -c)
wget -r [Link] Mirror website recursively
scp file user@host:/path Secure copy to remote host
scp user@host:/file . Secure copy from remote to current dir
ssh user@hostname Connect to remote host via SSH
ssh -p 2222 user@host SSH on non-standard port
rsync -avz src/ user@h:dst/ Efficient sync to remote (archive, verbose, compress)
nc -zv host 80 Test if port 80 is open (netcat)
Unix & Shell Scripting — Complete Reference Page 13
8. Archiving & Compression
tar
Command / Syntax Description & Notes
tar -cvf [Link] dir/ Create tar archive of directory
tar -xvf [Link] Extract tar archive
tar -czvf [Link] dir/ Create gzip-compressed archive (.[Link])
tar -xzvf [Link] Extract gzip archive
tar -cjvf [Link].bz2 dir/ Create bzip2 archive
tar -xjvf [Link].bz2 Extract bzip2 archive
tar -tvf [Link] List contents without extracting
tar -xzvf [Link] -C /dest Extract to specific directory
■ tar flags: c=create x=extract v=verbose f=filename z=gzip j=bzip2 t=list
gzip, bzip2, xz, zip
Command / Syntax Description & Notes
gzip [Link] Compress → [Link] (replaces original)
gzip -d [Link] Decompress gzip file
gunzip [Link] Same as gzip -d
gzip -k [Link] Compress keeping original file
gzip -9 [Link] Maximum compression level
bzip2 [Link] Compress with bzip2 (better ratio than gzip)
bunzip2 [Link].bz2 Decompress bzip2 file
xz [Link] Compress with xz (best ratio, slowest)
unxz [Link] Decompress xz file
zip [Link] file1 file2 Create zip archive
zip -r [Link] dir/ Zip directory recursively
unzip [Link] Extract zip archive
unzip -l [Link] List zip contents
Unix & Shell Scripting — Complete Reference Page 14
9. Disk & System Monitoring
Command / Syntax Description & Notes
df -h Disk space usage of all filesystems (human-readable)
df -h /home Disk space for specific filesystem
du -sh dir/ Total size of directory
du -h --max-depth=1 / Size of each item in root (1 level)
du -sh * | sort -rh Sort directories by size
free -h RAM and swap usage
vmstat 1 Virtual memory stats every 1 second
iostat CPU and I/O statistics
uptime System uptime and load averages
uname -a All system info: kernel, hostname, arch
hostname Show system hostname
whoami Show current username
id Show UID, GID, and group memberships
who Show logged-in users
w Show users and what they are doing
last Show login history
history Show command history
!42 Repeat command number 42 from history
!! Repeat last command
date Show current date and time
date +"%Y-%m-%d" Format date as YYYY-MM-DD
cal Display calendar for current month
env Print all environment variables
export VAR=value Set and export an environment variable
echo $PATH Print the PATH variable
Unix & Shell Scripting — Complete Reference Page 15
10. Redirection, Pipes & Special Characters
Standard Streams
Command / Syntax Description & Notes
stdin (0) Standard input — keyboard by default
stdout (1) Standard output — screen by default
stderr (2) Standard error — screen by default
Redirection Operators
Command / Syntax Description & Notes
cmd > file Redirect stdout to file (overwrite)
cmd >> file Redirect stdout to file (append)
cmd < file Read stdin from file
cmd 2> [Link] Redirect stderr to file
cmd 2>&1 Redirect stderr to same destination as stdout
cmd > out 2>&1 Both stdout and stderr to file
cmd &> file Bash shorthand: stdout + stderr to file
cmd 2>/dev/null Discard stderr (suppress error messages)
cmd > /dev/null 2>&1 Discard ALL output
cmd << EOF Here-document — multi-line stdin until EOF
cmd <<< "string" Here-string — feed string as stdin
Pipes & Command Chaining
Command / Syntax Description & Notes
cmd1 | cmd2 Pipe stdout of cmd1 to stdin of cmd2
cmd1 | cmd2 | cmd3 Chain multiple commands
cmd1 && cmd2 Run cmd2 ONLY if cmd1 succeeds (exit 0)
cmd1 || cmd2 Run cmd2 ONLY if cmd1 fails
cmd1 ; cmd2 Run cmd2 regardless of cmd1 exit status
(cmd1 ; cmd2) Run in a subshell
cmd1 | tee file Pipe and also save output to file
cmd1 | xargs cmd2 Pass cmd1 output as arguments to cmd2
Special Characters & Globbing
Command / Syntax Description & Notes
* Wildcard — matches any string (including empty)
Unix & Shell Scripting — Complete Reference Page 16
? Matches any single character
[abc] Match a, b, or c
[a-z] Match any lowercase letter
{a,b,c} Brace expansion — a, b, or c
~ Home directory shortcut
$VAR Variable expansion
$(cmd) Command substitution — replace with output of cmd
`cmd` Older command substitution syntax (avoid)
\ Escape next character (remove special meaning)
' ' Single quotes — no expansion whatsoever
" " Double quotes — allows $ and ` expansion
# Comment — rest of line ignored
; Command separator
Practical Examples
• Count errors in log: grep -c 'ERROR' /var/log/[Link]
• Top 10 memory processes: ps aux | sort -k4 -rn | head -10
• Unique IPs in access log: awk '{print $1}' [Link] | sort | uniq -c | sort -rn
• Find & replace in all .conf files: find /etc -name '*.conf' | xargs sed -i 's/old/new/g'
Unix & Shell Scripting — Complete Reference Page 17
11. Shell Scripting — Introduction
A shell script is a text file containing a sequence of shell commands that are executed in order. Scripts automate
repetitive tasks, system administration, and complex workflows.
Shebang Line
The first line of every script should specify the interpreter using #! (shebang):
#!/bin/bash # Use bash interpreter
#!/usr/bin/env bash # Preferred: portable, finds bash in PATH
#!/bin/sh # POSIX sh (more portable, fewer features)
Creating and Running a Script
Command / Syntax Description & Notes
nano [Link] Create script file in nano editor
chmod +x [Link] Make script executable
./[Link] Execute script from current directory
bash [Link] Run with bash explicitly (no +x needed)
bash -x [Link] Debug mode — print each command before executing
bash -n [Link] Syntax check only (no execution)
source [Link] Run in CURRENT shell (variables persist after)
. [Link] Same as source (POSIX)
Minimal Hello World Script
#!/usr/bin/env bash
# This is a comment
echo "Hello, World!"
echo "Current date: $(date)"
echo "Logged in as: $USER"
Script Exit Codes
Every command returns an exit code: 0 = success, non-zero = failure. Use $? to read the last exit code.
ls /nonexistent
echo "Exit code: $?" # prints: Exit code: 2
exit 0 # explicitly exit with success
exit 1 # exit with error
Unix & Shell Scripting — Complete Reference Page 18
12. Variables & Data Types
Bash is dynamically typed — variables are untyped by default (everything is a string). Types are determined by
context. Use declare for explicit types.
Variable Basics
name="Alice" # No spaces around =
age=25
echo $name # Alice
echo ${name} # Alice (braces recommended for clarity)
echo "Hello, $name" # Hello, Alice
echo 'Hello, $name' # Hello, \$name (no expansion in single quotes)
Data Types with declare
Command / Syntax Description & Notes
declare -i num=10 Integer — arithmetic errors enforced
declare -r PI=3.14 Read-only (constant) — cannot be changed
declare -l lower Lowercase — converts value to lowercase
declare -u upper Uppercase — converts value to uppercase
declare -a arr Indexed array (see Chapter 18)
declare -A assoc Associative array (hash/dict)
declare -x VAR=val Export variable to child processes
declare -p VAR Print variable attributes and value
Environment Variables
Environment variables are available to all child processes. Set with export.
export MYVAR='hello' # Export to child processes
export PATH=$PATH:/new/dir # Append to PATH
unset MYVAR # Delete variable
readonly CONST=42 # Declare read-only
Built-in Special Variables
Command / Syntax Description & Notes
$0 Name of the script
$1 - $9 Positional parameters — script arguments
${10} 10th argument (braces required for 2+ digits)
$# Number of arguments passed to script
$@ All arguments as separate quoted strings
$* All arguments as a single string
$? Exit status of last command
Unix & Shell Scripting — Complete Reference Page 19
$$ PID of current shell
$! PID of last background process
$- Current shell options flags
$_ Last argument of previous command
$RANDOM Random integer between 0 and 32767
$LINENO Current line number in the script
$BASH_VERSION Version of bash running
$HOME Home directory
$USER Current username
$HOSTNAME System hostname
$PWD Current working directory
$OLDPWD Previous working directory
$IFS Internal Field Separator (default: space tab newline)
$PATH Colon-separated list of command search directories
$SHELL Path to current shell
$TERM Terminal type
$EDITOR Default editor
$LANG Language/locale setting
Command Substitution
today=$(date +%Y-%m-%d) # Capture command output
files=$(ls *.txt)
lines=$(wc -l < [Link])
echo "There are $lines lines"
Arithmetic
a=5; b=3
echo $((a + b)) # 8
echo $((a * b)) # 15
echo $((a ** b)) # 125 (power)
echo $((a / b)) # 1 (integer division)
echo $((a % b)) # 2 (modulo)
((count++)) # Increment
((count--)) # Decrement
let result=a*b+2 # let for arithmetic
result=$(echo '3.14*2' | bc -l) # Float: use bc
■ Bash only does integer arithmetic natively. For floats, use bc or awk.
Unix & Shell Scripting — Complete Reference Page 20
13. Operators
String Comparison Operators (inside [ ] or [[ ]])
Command / Syntax Description & Notes
[ "$a" = "$b" ] Equal (POSIX, use = not ==)
[ "$a" == "$b" ] Equal (bash, same as =)
[ "$a" != "$b" ] Not equal
[ -z "$a" ] True if string is empty (zero length)
[ -n "$a" ] True if string is NOT empty
[[ $a < $b ]] Lexicographic less-than (use [[ ]])
[[ $a > $b ]] Lexicographic greater-than
[[ $a =~ regex ]] Regex match (bash only, [[ ]] required)
Integer Comparison Operators
Command / Syntax Description & Notes
[ $a -eq $b ] Equal
[ $a -ne $b ] Not equal
[ $a -lt $b ] Less than
[ $a -le $b ] Less than or equal
[ $a -gt $b ] Greater than
[ $a -ge $b ] Greater than or equal
File Test Operators
Command / Syntax Description & Notes
[ -e file ] File exists (any type)
[ -f file ] Exists and is a regular file
[ -d file ] Exists and is a directory
[ -L file ] Exists and is a symbolic link
[ -r file ] File is readable
[ -w file ] File is writable
[ -x file ] File is executable
[ -s file ] File exists and is NOT empty (size > 0)
[ -z file ] File is empty (zero size)
[ f1 -nt f2 ] f1 is newer than f2
[ f1 -ot f2 ] f1 is older than f2
Unix & Shell Scripting — Complete Reference Page 21
[ f1 -ef f2 ] f1 and f2 are the same file (hard link)
Logical Operators
Command / Syntax Description & Notes
[ cond1 ] && [ cond2 ] AND — both must be true
[ cond1 ] || [ cond2 ] OR — at least one true
[ ! condition ] NOT — negate condition
[[ cond1 && cond2 ]] AND inside [[ ]] (preferred in bash)
[[ cond1 || cond2 ]] OR inside [[ ]]
-a inside [ ] AND (POSIX, inside single [ ])
-o inside [ ] OR (POSIX, inside single [ ])
■ Use [[ ]] (double brackets) in bash scripts — safer, supports regex, no word splitting.
Unix & Shell Scripting — Complete Reference Page 22
14. Control Flow — if / elif / else
Basic if Syntax
if [ condition ]; then
# commands if condition is true
fi
if / else
if [ condition ]; then
echo "True"
else
echo "False"
fi
if / elif / else
if [ condition1 ]; then
echo "Condition 1 met"
elif [ condition2 ]; then
echo "Condition 2 met"
elif [ condition3 ]; then
echo "Condition 3 met"
else
echo "No condition met"
fi
Practical Examples
Example 1 — Check if file exists
#!/usr/bin/env bash
file="/etc/passwd"
if [ -f "$file" ]; then
echo "File exists: $file"
else
echo "File not found: $file"
fi
Example 2 — Compare numbers
#!/usr/bin/env bash
read -p "Enter a number: " num
if [ "$num" -lt 0 ]; then
echo "Negative"
elif [ "$num" -eq 0 ]; then
echo "Zero"
elif [ "$num" -le 100 ]; then
echo "Between 1 and 100"
Unix & Shell Scripting — Complete Reference Page 23
else
echo "Greater than 100"
fi
Example 3 — Check user input
#!/usr/bin/env bash
read -p "Enter username: " user
if [ -z "$user" ]; then
echo "Error: username cannot be empty"
exit 1
fi
echo "Welcome, $user!"
Example 4 — Using [[ ]] with regex
#!/usr/bin/env bash
read -p "Enter email: " email
if [[ "$email" =~ ^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$ ]]; then
echo "Valid email"
else
echo "Invalid email format"
fi
Example 5 — Short-circuit (one-liner)
[ -d "/backup" ] || mkdir /backup # Create dir if missing
[ -f "[Link]" ] && source [Link] # Source if exists
■ [[ ]] is preferred over [ ] in bash. Always quote variables: "$var" to prevent word splitting.
Unix & Shell Scripting — Complete Reference Page 24
15. Loops — for, while, until
for Loop — List Iteration
for item in item1 item2 item3; do
echo "$item"
done
for Loop — C-style
for ((i=1; i<=10; i++)); do
echo "Number: $i"
done
for Loop — Range with seq or brace expansion
for i in {1..10}; do echo $i; done # 1 to 10
for i in {0..20..5}; do echo $i; done # 0 5 10 15 20
for i in $(seq 1 2 10); do echo $i; done # 1 3 5 7 9
for Loop — Files & Command Output
for file in *.txt; do
echo "Processing: $file"
wc -l "$file"
done
for user in $(cat /etc/passwd | cut -d: -f1); do
echo "User: $user"
done
while Loop
while [ condition ]; do
# commands
done
Example — Count down
count=5
while [ $count -gt 0 ]; do
echo "Countdown: $count"
((count--))
done
echo "Liftoff!"
Example — Read file line by line
while IFS= read -r line; do
echo "Line: $line"
done < [Link]
Unix & Shell Scripting — Complete Reference Page 25
Example — Infinite loop with break
while true; do
read -p "Enter 'quit' to exit: " input
if [ "$input" = "quit" ]; then
break
fi
echo "You entered: $input"
done
until Loop
Runs while the condition is FALSE — opposite of while.
count=0
until [ $count -ge 5 ]; do
echo "count is $count"
((count++))
done
Loop Control
Command / Syntax Description & Notes
break Exit the loop immediately
break 2 Exit 2 levels of nested loops
continue Skip to next iteration
continue 2 Skip to next iteration of outer loop
Example — break and continue
for i in {1..10}; do
if [ $i -eq 5 ]; then continue; fi # skip 5
if [ $i -eq 8 ]; then break; fi # stop at 8
echo $i
done
# Output: 1 2 3 4 6 7
■ Always quote variables in loop conditions: [ "$var" = "value" ] not [ $var = value ]
Unix & Shell Scripting — Complete Reference Page 26
16. case Statement
The case statement is a multi-branch conditional — cleaner than many elif chains when testing a variable
against multiple patterns.
Syntax
case $variable in
pattern1)
# commands
;;
pattern2 | pattern3) # OR patterns
# commands
;;
*) # Default (wildcard)
# commands
;;
esac
Example 1 — Day of the week
#!/usr/bin/env bash
day=$(date +%A)
case $day in
Monday|Tuesday|Wednesday|Thursday|Friday)
echo "Weekday: $day"
;;
Saturday|Sunday)
echo "Weekend: $day"
;;
*)
echo "Unknown day"
;;
esac
Example 2 — Simple menu
#!/usr/bin/env bash
echo "1) Start service"
echo "2) Stop service"
echo "3) Restart service"
read -p "Choose [1-3]: " choice
case $choice in
1) echo "Starting..." ;;
2) echo "Stopping..." ;;
3) echo "Restarting..." ;;
*) echo "Invalid option" ; exit 1 ;;
Unix & Shell Scripting — Complete Reference Page 27
esac
Example 3 — File extension handler
#!/usr/bin/env bash
file="$1"
case ${file##*.} in # Extract extension
txt|md) echo "Text file" ;;
sh|bash) echo "Shell script" ;;
py) echo "Python script" ;;
jpg|png|gif) echo "Image file" ;;
*) echo "Unknown type" ;;
esac
■ Patterns support wildcards: * ? [abc]. Use | to match multiple patterns in one branch.
Unix & Shell Scripting — Complete Reference Page 28
17. Functions
Defining & Calling Functions
# Method 1 (preferred)
function greet() {
echo "Hello, $1!"
}
# Method 2 (POSIX compatible)
greet() {
echo "Hello, $1!"
}
greet "Alice" # Call the function
Parameters & Return Values
Functions receive arguments via $1, $2 … $n. Return values: use return N for exit code (0–255), or echo to
return strings.
add() {
local result=$(( $1 + $2 )) # local scope
echo $result # 'return' a string
}
sum=$(add 4 7) # Capture output
echo "Sum = $sum" # Sum = 11
local Variables
Variables inside functions are global by default. Use local to restrict scope.
counter=10
increment() {
local counter=0 # local copy
((counter++))
echo "Inside: $counter" # 1
}
increment
echo "Outside: $counter" # Still 10
Comprehensive Example
#!/usr/bin/env bash
# ■■ Logging helpers ■■■■■■■■■■■■■■■■■■■■■■
log_info() { echo "[INFO] $*"; }
log_error() { echo "[ERROR] $*" >&2; }
# ■■ Validate arguments ■■■■■■■■■■■■■■■■■■■
check_args() {
if [ $# -lt 2 ]; then
Unix & Shell Scripting — Complete Reference Page 29
log_error "Usage: $0 <src> <dst>"
return 1
fi
return 0
}
# ■■ Backup file ■■■■■■■■■■■■■■■■■■■■■■■■■■
backup_file() {
local src="$1"
local dst="$2"
if [ ! -f "$src" ]; then
log_error "Source not found: $src"
return 1
fi
cp -p "$src" "$dst" && log_info "Backed up to $dst"
}
# ■■ Main ■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■
check_args "$@" || exit 1
backup_file "$1" "$2"
■ Always use 'local' for variables inside functions to avoid polluting global scope.
Unix & Shell Scripting — Complete Reference Page 30
18. Arrays
Indexed Arrays
# Declare and initialize
fruits=("apple" "banana" "cherry")
nums=(1 2 3 4 5)
# Access elements (0-indexed)
echo ${fruits[0]} # apple
echo ${fruits[2]} # cherry
echo ${fruits[-1]} # cherry (last element, bash 4+)
# All elements
echo ${fruits[@]} # apple banana cherry
echo ${fruits[*]} # apple banana cherry
# Array length
echo ${#fruits[@]} # 3
# Modify element
fruits[1]="blueberry"
# Append element
fruits+=("date")
# Slice: elements 1 and 2 (offset 1, length 2)
echo ${fruits[@]:1:2}
# Delete element
unset fruits[2]
# Iterate
for fruit in "${fruits[@]}"; do
echo "Fruit: $fruit"
done
# Iterate with index
for i in "${!fruits[@]}"; do
echo "$i: ${fruits[$i]}"
done
Associative Arrays (Key-Value / Hash Maps) — bash 4+
declare -A person
person[name]="Alice"
person[age]=30
person[city]="Nairobi"
# Or initialize all at once
declare -A config=([host]="localhost" [port]="8080")
echo ${person[name]} # Alice
echo ${person[age]} # 30
# All keys
echo ${!person[@]} # name age city
Unix & Shell Scripting — Complete Reference Page 31
# All values
echo ${person[@]}
# Iterate key-value
for key in "${!person[@]}"; do
echo "$key = ${person[$key]}"
done
# Delete a key
unset person[city]
# Check if key exists
if [[ -v person[name] ]]; then
echo "name key exists"
fi
■ Associative arrays require bash version 4+. Check with: bash --version
Unix & Shell Scripting — Complete Reference Page 32
19. String Manipulation
Bash provides powerful built-in parameter expansion for strings, avoiding the need for external tools.
String Length & Slicing
str="Hello, World!"
echo ${#str} # Length: 13
echo ${str:7} # Slice from index 7: World!
echo ${str:7:5} # Slice 5 chars from 7: World
echo ${str: -6} # Last 6 chars: orld!
Substitution
path="/usr/local/bin/bash"
echo ${path/bash/sh} # Replace first 'bash' with 'sh'
echo ${path//i/I} # Replace ALL 'i' with 'I'
echo ${path/#\/usr/\/opt} # Replace prefix /usr with /opt
echo ${path/%bash/dash} # Replace suffix bash with dash
Prefix & Suffix Removal
filename="backup_2024-[Link]"
# Remove prefix (shortest match)
echo ${filename#*_} # [Link]
# Remove prefix (longest match)
echo ${filename##*/} # backup_2024-[Link]
# Remove suffix (shortest)
echo ${filename%.*} # backup_2024-[Link]
# Remove suffix (longest)
echo ${filename%%.*} # backup_2024-01-15
# Practical: extract extension
ext=${filename##*.} # gz
# Practical: strip extension
base=${filename%.[Link]} # backup_2024-01-15
Case Conversion (bash 4+)
name="hello world"
echo ${name^} # Hello world (first char upper)
echo ${name^^} # HELLO WORLD (all upper)
name2="HELLO WORLD"
echo ${name2,} # hELLO WORLD (first char lower)
echo ${name2,,} # hello world (all lower)
Default & Alternate Values
Unix & Shell Scripting — Complete Reference Page 33
Command / Syntax Description & Notes
${var:-default} Use default if var is unset or empty
${var:=default} Assign default if var is unset or empty
${var:+alternate} Use alternate if var IS set and non-empty
${var:?message} Print message and exit if var is unset
${var:-} Empty string if var is unset (no error)
# Examples
echo ${name:-"Anonymous"} # Anonymous if name is empty
echo ${port:=8080} # Sets port=8080 if unset
echo ${DEBUG:+"[DEBUG]"} # Prints [DEBUG] only if DEBUG is set
Unix & Shell Scripting — Complete Reference Page 34
20. File I/O in Scripts
Writing to Files
# Overwrite
echo "Hello" > [Link]
# Append
echo "World" >> [Link]
# Write multiple lines (heredoc)
cat > [Link] << EOF
host=localhost
port=8080
debug=false
EOF
# Write with printf (precise formatting)
printf "%s\n" "Line 1" "Line 2" "Line 3" > [Link]
Reading from Files
# Read entire file into variable
content=$(cat [Link])
# Read line by line (correct way)
while IFS= read -r line; do
echo ">> $line"
done < [Link]
# Read with filename as variable
input_file="[Link]"
while IFS=',' read -r col1 col2 col3; do
echo "Name: $col1 Age: $col2 City: $col3"
done < "$input_file"
# Read first line
read -r first_line < [Link]
echo "First line: $first_line"
# Read all lines into array
mapfile -t lines < [Link]
echo "Total lines: ${#lines[@]}"
echo "Line 3: ${lines[2]}"
File Checks Before I/O
#!/usr/bin/env bash
input="/path/to/[Link]"
output="/path/to/[Link]"
# Check input exists and is readable
if [ ! -f "$input" ]; then
echo "Error: $input not found" >&2
Unix & Shell Scripting — Complete Reference Page 35
exit 1
fi
if [ ! -r "$input" ]; then
echo "Error: $input not readable" >&2
exit 1
fi
# Process
while IFS= read -r line; do
echo "${line^^}" # uppercase
done < "$input" > "$output"
echo "Done. Output saved to $output"
■ IFS= (empty IFS) and -r (raw mode) prevent stripping of leading/trailing spaces and backslash processing.
Unix & Shell Scripting — Complete Reference Page 36
21. Error Handling & Debugging
set Options for Safety
Command / Syntax Description & Notes
set -e Exit immediately if any command fails (errexit)
set -u Treat unset variables as errors (nounset)
set -o pipefail Pipe fails if ANY command in the pipe fails
set -x Print each command before executing (xtrace/debug)
set -v Print each line of script as it is read
set +e Turn off -e (temporarily allow failures)
set +x Turn off debugging
set -euo pipefail The professional default — use at top of scripts
#!/usr/bin/env bash
set -euo pipefail
IFS=$'\n\t' # Safer IFS
Trapping Errors & Signals
# Run cleanup on exit (always)
cleanup() {
echo "Cleaning up..."
rm -f /tmp/myscript_$$
}
trap cleanup EXIT
# Catch errors
trap 'echo "Error on line $LINENO"' ERR
# Handle Ctrl+C
trap 'echo "Interrupted!"; exit 1' INT
# Multiple signals
trap cleanup EXIT ERR INT TERM
Custom Error Handling
die() {
local msg="$1"
local code="${2:-1}" # Default exit code 1
echo "[ERROR] $msg" >&2
exit "$code"
}
[ -f "$config" ] || die "Config file not found: $config" 2
Debugging Techniques
Unix & Shell Scripting — Complete Reference Page 37
Command / Syntax Description & Notes
bash -x [Link] Debug entire script — shows each command
bash -n [Link] Syntax check without running
bash -v [Link] Verbose — print script as it reads
set -x … set +x Enable/disable debug for a section
echo "VAR=$VAR" >&2 Print debug info to stderr
PS4='+ ${BASH_SOURCE}:${LINENO}: ' Show file and line in debug output
# Inline debug section
set -x
suspicious_command "$arg"
set +x
■ Always write error messages to stderr: echo 'Error' >&2 so they can be separated from stdout.
Unix & Shell Scripting — Complete Reference Page 38
22. Regular Expressions
Regular expressions (regex) are patterns used to match text. Bash uses them in [[ =~ ]], grep, sed, and awk.
Basic Regex Metacharacters
Command / Syntax Description & Notes
. Match any single character (except newline)
* 0 or more of preceding element
+ 1 or more of preceding element (ERE)
? 0 or 1 of preceding element (ERE)
^ Start of line
$ End of line
[abc] Character class — matches a, b, or c
[^abc] Negated class — matches anything except a, b, c
[a-z] Range — any lowercase letter
\d Digit (Perl/PCRE: use [0-9] in POSIX)
\w Word character [a-zA-Z0-9_]
\s Whitespace (space, tab, newline)
\b Word boundary
{n} Exactly n occurrences
{n,m} Between n and m occurrences
(abc) Group — treat as unit
a|b Alternation — a OR b (ERE)
\ Escape metacharacter
Common Regex Patterns
Command / Syntax Description & Notes
^[0-9]+$ Integer (digits only)
^-?[0-9]+(\.[0-9]+)?$ Number (optional decimal)
Email address
[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,}
^(https?|ftp)://[^\s/$.?#].[^\s]*$ URL
^[0-9]{1,3}(\.[0-9]{1,3}){3}$ IPv4 address
^[0-9]{4}-[0-9]{2}-[0-9]{2}$ Date YYYY-MM-DD
^[A-Z][a-z]+$ Capitalized word
#[0-9a-fA-F]{6} Hex colour code
Unix & Shell Scripting — Complete Reference Page 39
Using Regex in Bash
# In [[ ]] (POSIX ERE, no quotes around pattern)
if [[ "$email" =~ ^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$ ]]; then
echo "Valid email"
fi
# Capture groups — stored in ${BASH_REMATCH[@]}
[[ "2024-01-15" =~ ^([0-9]{4})-([0-9]{2})-([0-9]{2})$ ]]
echo "Year: ${BASH_REMATCH[1]}" # 2024
echo "Month: ${BASH_REMATCH[2]}" # 01
echo "Day: ${BASH_REMATCH[3]}" # 15
# grep with ERE
grep -E '^[0-9]{3}-[0-9]{4}$' [Link]
# sed with regex
sed -E 's/[0-9]{4}/YEAR/g' [Link]
# awk with regex
awk '/^[A-Z]/' [Link] # Lines starting with uppercase
Unix & Shell Scripting — Complete Reference Page 40
23. Advanced Topics & Best Practices
User Input with read
read -p "Enter name: " name
read -s -p "Password: " pass # -s: silent (no echo)
read -t 10 -p "Answer (10s): " ans # -t: timeout
read -n 1 -p "Press a key: " key # -n: read N chars
read -a arr -p "Enter words: " # read into array
select — Interactive Menus
PS3="Choose an option: "
select option in "Start" "Stop" "Restart" "Quit"; do
case $option in
Start) echo "Starting..." ;;
Stop) echo "Stopping..." ;;
Restart) echo "Restarting..." ;;
Quit) break ;;
*) echo "Invalid" ;;
esac
done
Process Substitution
# Use command output as a file
diff <(sort [Link]) <(sort [Link])
while read line; do echo $line; done < <(ls -la)
Here Documents & Here Strings
# Heredoc — multi-line text as stdin
cat << EOF
Line 1
Line 2
EOF
# Heredoc with variable expansion suppressed
cat << 'EOF'
No \$expansion here
EOF
# Here string — single string as stdin
grep 'hello' <<< "hello world"
Script Arguments with getopts
#!/usr/bin/env bash
usage() { echo "Usage: $0 [-v] [-f file] [-n name]"; exit 1; }
verbose=false; file=""; name=""
Unix & Shell Scripting — Complete Reference Page 41
while getopts ":vf:n:h" opt; do
case $opt in
v) verbose=true ;;
f) file="$OPTARG" ;;
n) name="$OPTARG" ;;
h) usage ;;
:) echo "Option -$OPTARG requires argument"; usage ;;
?) echo "Unknown option: -$OPTARG"; usage ;;
esac
done
shift $((OPTIND - 1)) # Remove parsed options, $@ = remaining args
Subshells & Command Groups
# Subshell — changes don't affect parent
(cd /tmp && ls) # cd only affects subshell
echo $PWD # original dir unchanged
# Command group — runs in current shell
{ echo "line1"; echo "line2"; } > [Link]
eval & exec
# eval — execute string as command (use carefully)
cmd="ls -la"
eval "$cmd"
# exec — replace current shell with command (no return)
exec /usr/bin/python3 [Link]
Best Practices Summary
• Always start with: #!/usr/bin/env bash and set -euo pipefail
• Quote variables: Use "$var" not $var to prevent word splitting
• Use [[ ]] not [ ] in bash for safer string comparisons
• Local variables in functions: declare local to avoid side effects
• Use $() not backticks for command substitution — easier to nest
• Check exit codes: test commands and handle failures gracefully
• Write to stderr: echo 'error' >&2 for error/debug messages
• Use meaningful names: max_retries not mr; log_error() not le()
• Add usage() function: describe flags and arguments clearly
• Trap EXIT: always clean up temp files with trap cleanup EXIT
• Avoid eval: it executes arbitrary code — use arrays instead
• Test on /bin/sh too: if POSIX portability matters
Commonly Used One-Liners
Command / Syntax Description & Notes
Batch
for f in *.jpg; do convert $f ${f%.jpg}.png; convert images
done
Unix & Shell Scripting — Complete Reference Page 42
Count all Python lines
find . -name '*.py' | xargs wc -l | tail -1
Top|10
history | awk '{print $2}' | sort | uniq -c commands
sort -rn |used
head
Diff remote
diff <(ssh host1 cat /etc/hosts) <(ssh host2 files
cat /etc/hosts)
watch -n 2 df -h Monitor disk every 2s
while sleep 1; do clear; df -h; done Refresh disk info loop
python3 -m [Link] 8080 Instant HTTP file server
nc -l 9999 | bash Simple remote shell (careful!)
End of Unix & Shell Scripting Complete Reference Guide
Unix & Shell Scripting — Complete Reference Page 43