Comprehensive Reference Guide โ Detailed examples with explanations for system administrators
Bash provides powerful built-in capabilities for arithmetic operations and date/time manipulation. This guide covers the essential techniques every Linux system administrator needs to master, from basic calculations to complex date arithmetic used in log rotation, backup scheduling, and system monitoring scripts.
Arithmetic: Bash supports integer arithmetic natively through $(( )), let, and expr. For floating-point math, use bc or awk.
Date/Time: The date command is incredibly versatile, supporting format strings, date arithmetic, parsing, and timezone conversions.
| Method | Syntax | Use Case | Notes |
|---|---|---|---|
| $(( )) | result=$((5 + 3)) | Most common, recommended | POSIX compliant, returns value |
| let | let result=5+3 | Variable assignment | No spaces around operators |
| expr | result=$(expr 5 + 3) | Legacy scripts | Requires spaces, slower |
| bc | echo "5.5 + 3.2" | bc | Floating-point math | External command, very powerful |
| Operator | Description | Example | Result |
|---|---|---|---|
+ | Addition | $((5 + 3)) | 8 |
- | Subtraction | $((10 - 4)) | 6 |
* | Multiplication | $((6 * 7)) | 42 |
/ | Division (integer) | $((17 / 5)) | 3 |
% | Modulo (remainder) | $((17 % 5)) | 2 |
** | Exponentiation | $((2 ** 10)) | 1024 |
++ | Increment | $((i++)) | i + 1 |
-- | Decrement | $((i--)) | i - 1 |
<< | Bit shift left | $((1 << 4)) | 16 |
>> | Bit shift right | $((16 >> 2)) | 4 |
& | Bitwise AND | $((12 & 10)) | 8 |
| | Bitwise OR | $((12 | 10)) | 14 |
Fundamental arithmetic operations commonly used in system scripts.
#!/bin/bash
# Basic arithmetic operations with variables
a=15
b=4
sum=$((a + b))
diff=$((a - b))
product=$((a * b))
quotient=$((a / b))
remainder=$((a % b))
echo "Variables: a=$a, b=$b"
echo "Sum: $sum"
echo "Difference: $diff"
echo "Product: $product"
echo "Quotient: $quotient (integer division)"
echo "Remainder: $remainder"
# Exponentiation
power=$((2 ** 8))
echo "2^8 = $power"
# Compound operations
compound=$(( (a + b) * 2 - a / b ))
echo "Compound: $compound"
#!/bin/bash
# Increment, decrement, and compound assignment
count=10
echo "Initial: $count"
# Post-increment (use then increment)
echo "Post-increment: $((count++))"
echo "After: $count"
# Pre-increment (increment then use)
echo "Pre-increment: $((++count))"
# Compound assignments
value=100
((value += 25))
echo "After += 25: $value"
((value *= 2))
echo "After *= 2: $value"
((value /= 5))
echo "After /= 5: $value"
#!/bin/bash
# Floating-point arithmetic with bc
# Basic division
result=$(echo "scale=2; 15 / 4" | bc)
echo "15 / 4 = $result"
# Percentage calculation
total=847
used=523
percent=$(echo "scale=2; ($used / $total) * 100" | bc)
echo "Usage: ${percent}%"
# Square root
sqrt=$(echo "scale=4; sqrt(2)" | bc)
echo "sqrt(2) = $sqrt"
# Pi calculation
pi=$(echo "scale=10; 4*a(1)" | bc -l)
echo "Pi = $pi"
# Disk usage example
disk_total=500000000000
disk_used=327000000000
disk_percent=$(echo "scale=1; ($disk_used / $disk_total) * 100" | bc)
echo "Disk: ${disk_percent}%"
#!/bin/bash
# Bitwise operations for permissions and flags
# Permission bits
read_bit=4
write_bit=2
exec_bit=1
rwx=$((read_bit | write_bit | exec_bit))
rw=$((read_bit | write_bit))
echo "rwx = $rwx, rw = $rw"
# Check permission
perms=5 # r-x
if (( perms & write_bit )); then
echo "Write IS set"
else
echo "Write NOT set"
fi
# Powers of 2 with shift
for i in {0..7}; do
echo "2^$i = $((1 << i))"
done
# Network mask from CIDR
cidr=24
mask=$(( 0xFFFFFFFF << (32 - cidr) ))
echo "/$cidr mask: $(( (mask >> 24) & 0xFF )).$(( (mask >> 16) & 0xFF )).$(( (mask >> 8) & 0xFF )).$(( mask & 0xFF ))"
#!/bin/bash
# Arithmetic in loops and arrays
# C-style for loop - sum 1 to 10
sum=0
for ((i=1; i<=10; i++)); do
((sum += i))
done
echo "Sum 1-10: $sum"
# Array statistics
data=(23 45 67 89 12 34 56 78 90 11)
count=${#data[@]}
sum=0
min=${data[0]}
max=${data[0]}
for val in "${data[@]}"; do
((sum += val))
((val < min)) && min=$val
((val > max)) && max=$val
done
avg=$(echo "scale=2; $sum / $count" | bc)
echo "Count: $count, Sum: $sum"
echo "Min: $min, Max: $max, Avg: $avg"
# Fibonacci
fib[0]=0; fib[1]=1
for ((i=2; i<10; i++)); do
((fib[i] = fib[i-1] + fib[i-2]))
done
echo "Fibonacci: ${fib[*]}"
#!/bin/bash
# Arithmetic comparisons
value=42
# Using (( )) for tests
if ((value > 40)); then
echo "$value > 40 is TRUE"
fi
# Range check
if ((value >= 40 && value <= 50)); then
echo "$value is in range 40-50"
fi
# Practical: disk warning
disk=78
if ((disk >= 90)); then
echo "CRITICAL: ${disk}%"
elif ((disk >= 70)); then
echo "WARNING: ${disk}%"
else
echo "OK: ${disk}%"
fi
# Divisibility (FizzBuzz style)
for n in {1..15}; do
if ((n % 3 == 0 && n % 5 == 0)); then
echo "$n: FizzBuzz"
elif ((n % 3 == 0)); then
echo "$n: Fizz"
elif ((n % 5 == 0)); then
echo "$n: Buzz"
fi
done
#!/bin/bash
# Number base conversions
# Different base inputs
echo "Decimal 255: 255"
echo "Hex 0xFF: $((0xFF))"
echo "Octal 0377: $((0377))"
echo "Binary 2#11111111: $((2#11111111))"
# Decimal to other bases
value=200
printf "Decimal %d = Hex %X = Octal %o\n" $value $value $value
# IP to integer
ip="192.168.1.100"
IFS='.' read -r a b c d <<< "$ip"
int=$(( (a << 24) + (b << 16) + (c << 8) + d ))
printf "IP %s = %d\n" "$ip" "$int"
# Chmod octal to symbolic
for perm in 755 644 700; do
mode=$((8#$perm))
sym=""
for i in 0 1 2; do
d=$(( (mode >> (2-i)*3) & 7 ))
((d & 4)) && sym+="r" || sym+="-"
((d & 2)) && sym+="w" || sym+="-"
((d & 1)) && sym+="x" || sym+="-"
done
echo "$perm = $sym"
done
#!/bin/bash
# Random numbers and conversions
# Random in range
echo "Random 1-100: $(( RANDOM % 100 + 1 ))"
echo "Random die: $(( RANDOM % 6 + 1 ))"
# Bytes to human readable
bytes_to_human() {
local b=$1
local units=("B" "KB" "MB" "GB" "TB")
local u=0
while ((b >= 1024 && u < 4)); do
b=$(echo "scale=2; $b / 1024" | bc)
((u++))
done
echo "$b ${units[$u]}"
}
echo "1048576 bytes = $(bytes_to_human 1048576)"
echo "5368709120 bytes = $(bytes_to_human 5368709120)"
# Seconds to human
secs=90061
days=$((secs / 86400))
hrs=$(( (secs % 86400) / 3600 ))
mins=$(( (secs % 3600) / 60 ))
echo "$secs seconds = ${days}d ${hrs}h ${mins}m"
# Percentage change
old=150; new=180
change=$((new - old))
pct=$(echo "scale=1; ($change * 100) / $old" | bc)
echo "Change: $old โ $new = ${pct}%"
#!/bin/bash
# Advanced arithmetic with awk
# Basic
echo | awk '{print "15/4 =", 15/4}'
echo | awk '{print "2^10 =", 2^10}'
# Math functions
awk 'BEGIN {
print "sin(1) =", sin(1)
print "sqrt(2) =", sqrt(2)
print "exp(1) =", exp(1)
}'
# Statistics from data
echo "23 45 67 89 12" | tr ' ' '\n' | awk '
{
sum += $1
count++
if (NR==1 || $1 < min) min=$1
if (NR==1 || $1 > max) max=$1
}
END {
print "Count:", count
print "Sum:", sum
print "Mean:", sum/count
print "Min:", min, "Max:", max
}'
#!/bin/bash
# System resource calculations
echo "=== Memory Analysis ==="
total_kb=16384000
used_kb=12582912
free_kb=$((total_kb - used_kb))
total_gb=$(echo "scale=2; $total_kb / 1048576" | bc)
used_pct=$(echo "scale=1; ($used_kb * 100) / $total_kb" | bc)
echo "Total: ${total_gb} GB"
echo "Used: ${used_pct}%"
if (( $(echo "$used_pct > 90" | bc -l) )); then
echo "Status: CRITICAL"
elif (( $(echo "$used_pct > 75" | bc -l) )); then
echo "Status: WARNING"
else
echo "Status: OK"
fi
echo ""
echo "=== CPU Load ==="
cores=8
load=4.25
util=$(echo "scale=0; ($load * 100) / $cores" | bc)
echo "Load: $load on $cores cores = ${util}%"
echo ""
echo "=== Network Rate ==="
rx_start=10485760000
rx_end=10737418240
interval=60
rx_rate=$(( (rx_end - rx_start) / interval ))
rx_mbps=$(echo "scale=2; ($rx_rate * 8) / 1000000" | bc)
echo "RX: $rx_mbps Mbps"
date โ Display current date/time
date +FORMAT โ Custom format
date -d "STRING" โ Parse date string
date -d "DATE +/- OFFSET" โ Date arithmetic
| Specifier | Description | Example |
|---|---|---|
%Y | 4-digit year | 2025 |
%m | Month (01-12) | 11 |
%d | Day (01-31) | 18 |
%H | Hour (00-23) | 14 |
%M | Minute (00-59) | 30 |
%S | Second (00-59) | 45 |
%A | Full weekday | Tuesday |
%B | Full month | November |
%s | Unix epoch | 1731958245 |
%Z | Timezone | EST |
%z | TZ offset | -0500 |
#!/bin/bash
# Basic date formatting
echo "Default: $(date)"
echo "UTC: $(date -u)"
# Common formats
echo "ISO 8601: $(date +%Y-%m-%dT%H:%M:%S%z)"
echo "ISO Date: $(date +%Y-%m-%d)"
echo "US Date: $(date +%m/%d/%Y)"
echo "EU Date: $(date +%d/%m/%Y)"
# For filenames
echo "Filename: $(date +%Y%m%d_%H%M%S)"
echo "Daily: $(date +%Y-%m-%d)"
# Time formats
echo "24-hour: $(date +%H:%M:%S)"
echo "12-hour: $(date '+%I:%M:%S %p')"
# Special
echo "Epoch: $(date +%s)"
echo "Week: $(date +%W)"
echo "Day of year: $(date +%j)"
# Practical
echo "[$(date '+%Y-%m-%d %H:%M:%S')] Log message"
echo "backup-$(date +%Y%m%d-%H%M%S).tar.gz"
#!/bin/bash
# Date arithmetic with -d
echo "Today: $(date '+%Y-%m-%d')"
# Adding time
echo "Tomorrow: $(date -d 'tomorrow' '+%Y-%m-%d')"
echo "+3 days: $(date -d '+3 days' '+%Y-%m-%d')"
echo "+1 week: $(date -d '+1 week' '+%Y-%m-%d')"
echo "+1 month: $(date -d '+1 month' '+%Y-%m-%d')"
echo "+1 year: $(date -d '+1 year' '+%Y-%m-%d')"
# Subtracting
echo "Yesterday: $(date -d 'yesterday' '+%Y-%m-%d')"
echo "-7 days: $(date -d '-7 days' '+%Y-%m-%d')"
echo "-30 days: $(date -d '-30 days' '+%Y-%m-%d')"
echo "-3 months: $(date -d '-3 months' '+%Y-%m-%d')"
# Time arithmetic
echo "+2 hours: $(date -d '+2 hours' '+%H:%M:%S')"
echo "-30 min: $(date -d '-30 minutes' '+%H:%M:%S')"
# Retention dates
echo "Delete before:"
echo " 7 days: $(date -d '-7 days' '+%Y-%m-%d')"
echo " 30 days: $(date -d '-30 days' '+%Y-%m-%d')"
echo " 90 days: $(date -d '-90 days' '+%Y-%m-%d')"
#!/bin/bash
# Date parsing and conversion
# Various input formats
echo "From 'Nov 18, 2025': $(date -d 'Nov 18, 2025' '+%Y-%m-%d')"
echo "From '11/18/2025': $(date -d '11/18/2025' '+%Y-%m-%d')"
echo "From '2025-11-18': $(date -d '2025-11-18' '+%Y-%m-%d')"
# Format conversion
us_date="11/18/2025"
iso=$(date -d "$us_date" '+%Y-%m-%d')
echo "US โ ISO: $us_date โ $iso"
# Epoch conversions
epoch=$(date +%s)
echo "Now: $epoch"
echo "From epoch: $(date -d @1731958245 '+%Y-%m-%d %H:%M:%S')"
human="2025-11-18 14:30:45"
to_epoch=$(date -d "$human" '+%s')
echo "'$human' โ $to_epoch"
# Parse syslog format
syslog="Nov 18 14:30:45"
parsed=$(date -d "$syslog" '+%Y-%m-%d %H:%M:%S')
echo "Syslog: $syslog โ $parsed"
date -d @1731958245
#!/bin/bash
# Time difference calculations
date1="2025-01-01"
date2="2025-11-18"
epoch1=$(date -d "$date1" '+%s')
epoch2=$(date -d "$date2" '+%s')
diff=$((epoch2 - epoch1))
echo "From $date1 to $date2:"
echo " $((diff / 86400)) days"
echo " $((diff / 3600)) hours"
# Age calculation
birth="1982-06-25"
today=$(date '+%Y-%m-%d')
age_days=$(( ($(date -d "$today" '+%s') - $(date -d "$birth" '+%s')) / 86400 ))
echo "Age: $((age_days / 365)) years"
# Days until event
event="2025-12-25"
now=$(date '+%s')
event_epoch=$(date -d "$event" '+%s')
days=$(( (event_epoch - now) / 86400 ))
echo "Days until $event: $days"
# Script timing
start=$(date '+%s')
sleep 1
end=$(date '+%s')
echo "Elapsed: $((end - start)) seconds"
#!/bin/bash
# Timezone operations
echo "Local: $(date '+%H:%M %Z')"
echo "UTC: $(TZ=UTC date '+%H:%M %Z')"
echo ""
echo "US Timezones:"
echo " Eastern: $(TZ=America/New_York date '+%H:%M %Z')"
echo " Central: $(TZ=America/Chicago date '+%H:%M %Z')"
echo " Mountain: $(TZ=America/Denver date '+%H:%M %Z')"
echo " Pacific: $(TZ=America/Los_Angeles date '+%H:%M %Z')"
echo ""
echo "International:"
echo " London: $(TZ=Europe/London date '+%H:%M %Z')"
echo " Tokyo: $(TZ=Asia/Tokyo date '+%H:%M %Z')"
# Convert specific time
eastern="2025-11-18 14:00:00"
epoch=$(TZ=America/New_York date -d "$eastern" '+%s')
echo ""
echo "Meeting: $eastern Eastern"
echo " Pacific: $(TZ=America/Los_Angeles date -d @$epoch '+%H:%M %Z')"
echo " UTC: $(TZ=UTC date -d @$epoch '+%H:%M %Z')"
echo " Tokyo: $(TZ=Asia/Tokyo date -d @$epoch '+%H:%M %Z (%Y-%m-%d)')"
#!/bin/bash
# Weekday operations
echo "Today: $(date '+%A') ($(date '+%u'))"
# Find weekdays
echo "Next Monday: $(date -d 'next monday' '+%Y-%m-%d')"
echo "Last Friday: $(date -d 'last friday' '+%Y-%m-%d')"
# Check weekend
dow=$(date '+%u')
if ((dow >= 6)); then
echo "It's the weekend!"
else
echo "It's a weekday"
fi
# First/last of month
echo "First of month: $(date -d "$(date '+%Y-%m-01')" '+%Y-%m-%d (%A)')"
echo "Last of month: $(date -d "$(date '+%Y-%m-01') +1 month -1 day" '+%Y-%m-%d (%A)')"
# Business days check
is_weekend() {
(($(date -d "$1" '+%u') >= 6))
}
# Count business days
start="2025-11-01"
end="2025-11-18"
bdays=0
curr="$start"
while [[ "$curr" < "$end" ]]; do
is_weekend "$curr" || ((bdays++))
curr=$(date -d "$curr +1 day" '+%Y-%m-%d')
done
echo "Business days $start to $end: $bdays"
#!/bin/bash
# Log rotation filenames
echo "=== Filename Formats ==="
echo "Daily: app-$(date '+%Y-%m-%d').log"
echo "Hourly: app-$(date '+%Y-%m-%d_%H').log"
echo "Full: app-$(date '+%Y%m%d_%H%M%S').log"
echo "Weekly: app-$(date '+%Y-W%W').log"
echo "Monthly: app-$(date '+%Y-%m').log"
echo ""
echo "=== Rotation Names ==="
base="application.log"
for i in {1..5}; do
echo "${base}.$(date -d "-$i days" '+%Y-%m-%d')"
done
echo ""
echo "=== Backup Naming ==="
echo "Daily: backup-$(date '+%Y-%m-%d').tar.gz"
echo "Weekly: backup-$(date -d 'last sunday' '+%Y-%m-%d').tar.gz"
echo "Monthly: backup-$(date '+%Y-%m').tar.gz"
echo ""
echo "=== Cleanup Schedule ==="
echo "Delete daily older than: $(date -d '-7 days' '+%Y-%m-%d')"
echo "Delete weekly older than: $(date -d '-4 weeks' '+%Y-%m-%d')"
echo "Delete monthly older than: $(date -d '-12 months' '+%Y-%m-%d')"
#!/bin/bash
# Natural language dates
echo "=== Basic ==="
echo "now: $(date -d 'now' '+%Y-%m-%d %H:%M')"
echo "today: $(date -d 'today' '+%Y-%m-%d')"
echo "yesterday: $(date -d 'yesterday' '+%Y-%m-%d')"
echo "tomorrow: $(date -d 'tomorrow' '+%Y-%m-%d')"
echo ""
echo "=== Time Terms ==="
echo "noon: $(date -d 'noon' '+%H:%M')"
echo "midnight: $(date -d 'midnight' '+%H:%M')"
echo ""
echo "=== Complex ==="
echo "3 days ago: $(date -d '3 days ago' '+%Y-%m-%d')"
echo "2 weeks hence: $(date -d '2 weeks hence' '+%Y-%m-%d')"
echo "next year: $(date -d 'next year' '+%Y-%m-%d')"
echo ""
echo "=== Report Periods ==="
echo "Today: $(date '+%Y-%m-%d')"
echo "This week: $(date -d 'last monday' '+%Y-%m-%d') to $(date -d 'next sunday' '+%Y-%m-%d')"
echo "This month: $(date '+%Y-%m-01') to $(date -d "$(date '+%Y-%m-01') +1 month -1 day" '+%Y-%m-%d')"
echo ""
echo "=== Quarter ==="
month=$(date '+%-m')
quarter=$(( (month - 1) / 3 + 1 ))
echo "Current: Q$quarter"
#!/bin/bash
# Nanosecond timing
echo "Epoch: $(date '+%s')"
echo "With ns: $(date '+%s%N')"
echo "Milliseconds: $(date '+%s%3N')"
# Benchmark function
benchmark() {
local start=$(date '+%s%N')
"$@"
local end=$(date '+%s%N')
local ms=$(( (end - start) / 1000000 ))
echo "Elapsed: ${ms}ms"
}
benchmark sleep 0.1
# Code timing
start=$(date '+%s%N')
for i in {1..1000}; do echo "$i" > /dev/null; done
end=$(date '+%s%N')
echo "Loop: $(( (end - start) / 1000 )) ยตs"
# Unique IDs
echo "ID: $(date '+%s%N')-$$-$RANDOM"
# Using SECONDS
SECONDS=0
sleep 1
echo "SECONDS: $SECONDS"
#!/bin/bash
# Maintenance scheduler
echo "=== Current Time ==="
echo "Local: $(date '+%Y-%m-%d %H:%M:%S %Z')"
echo "UTC: $(date -u '+%Y-%m-%d %H:%M:%S %Z')"
echo "Day: $(date '+%j')/365, Week: $(date '+%W')/52"
echo ""
echo "=== Maintenance Window ==="
hour=$(date '+%H')
dow=$(date '+%u')
if ((dow >= 6)); then
echo "โ Weekend - extended window"
elif ((hour >= 2 && hour < 4)); then
echo "โ In window (2-4 AM)"
else
echo "โ Outside window"
fi
echo ""
echo "=== Retention Policy ==="
printf "%-20s %s\n" "Daily backups:" "$(date -d '-7 days' '+%Y-%m-%d')"
printf "%-20s %s\n" "Weekly backups:" "$(date -d '-4 weeks' '+%Y-%m-%d')"
printf "%-20s %s\n" "Monthly backups:" "$(date -d '-12 months' '+%Y-%m-%d')"
echo ""
echo "=== Upcoming ==="
today=$(date '+%s')
for event in "2025-11-27:Thanksgiving" "2025-12-25:Christmas" "2026-01-01:New Year"; do
IFS=':' read -r dt name <<< "$event"
days=$(( ($(date -d "$dt" '+%s') - today) / 86400 ))
echo " $name: $days days"
done
echo ""
echo "=== Global Team ==="
for loc in "HQ:America/New_York" "West:America/Los_Angeles" "EU:Europe/London"; do
IFS=':' read -r n tz <<< "$loc"
printf " %-6s %s\n" "$n" "$(TZ=$tz date '+%H:%M %Z')"
done
$(( )) for integer arithmeticbc for floating-pointdeclare -i for integer variablesexpr (slow, quirky)$[ ] (deprecated)%Y-%m-%dT%H:%M:%S%z)America/New_York)# Safe arithmetic with default
result=${value:-0}
((result++))
# Floating-point
result=$(echo "scale=2; $a / $b" | bc)
# Dated filename
file="backup-$(date '+%Y%m%d-%H%M%S').tar.gz"
# Time difference
elapsed=$(($(date '+%s') - start))
# Validate date
date -d "$input" &>/dev/null && echo "Valid"
# Last day of month
last=$(date -d "$(date '+%Y-%m-01') +1 month -1 day" '+%d')