Bash System Information
Use uname, df, du, ps, lsof, netstat/ss, and systemctl to inspect and monitor a Linux system from Bash scripts.
uname — System Identification
uname reports basic facts about the operating system and hardware. In scripts, its most important use is detecting the OS and CPU architecture so you can download the right binary or take the right code path for the current system.
uname -a # all info: kernel name, hostname, release, version, machine type
uname -s # kernel name: Linux
uname -r # kernel release: 6.1.0-28-amd64
uname -m # machine hardware: x86_64, aarch64
uname -n # network hostname
# Detect OS type and branch accordingly
OS=$(uname -s)
case "$OS" in
Linux) echo "Running on Linux" ;;
Darwin) echo "Running on macOS" ;;
*) echo "Unknown OS: $OS" ;;
esac
# Detect CPU architecture for downloading the right binary
ARCH=$(uname -m)
case "$ARCH" in
x86_64) ARCH_LABEL="amd64" ;;
aarch64) ARCH_LABEL="arm64" ;;
armv7l) ARCH_LABEL="armv7" ;;
*) echo "Unsupported arch: $ARCH" >&2; exit 1 ;;
esac
URL="https://releases.example.com/app-linux-${ARCH_LABEL}.tar.gz"
echo "Downloading: $URL"
df — Disk Free Space
df reports how much space is used and available on each mounted filesystem. In monitoring scripts, it is the primary tool for alerting on disks that are filling up before they cause outages.
df -h # human-readable sizes (KB, MB, GB)
df -h / # check a specific filesystem
df -i # inode usage — a filesystem can be "full" on inodes even with free bytes
df -T # include the filesystem type in the output
# In a script: check disk usage and alert if over a threshold
check_disk_space() {
local mount="${1:-/}"
local threshold="${2:-80}"
local usage
# awk extracts the percentage column and strips the % sign
usage=$(df -h "$mount" | awk 'NR==2 {gsub(/%/,""); print $5}')
if (( usage >= threshold )); then
echo "ALERT: disk usage on $mount is ${usage}% (threshold: ${threshold}%)" >&2
return 1
fi
echo "OK: disk usage on $mount is ${usage}%"
}
check_disk_space "/" 80
check_disk_space "/var" 90
du — Disk Usage
du measures how much space a directory or file actually consumes on disk. It is the right tool for finding what is consuming space when df shows a disk is nearly full.
du -sh /var/log # summary of total directory size
du -sh /var/log/* # size of each item inside a directory
du -h --max-depth=1 /var # one level deep, human-readable sizes
du -sh * | sort -h # sort by size, smallest first
du -sh * | sort -rh # sort by size, largest first
# Find the top 10 largest directories under /var
du -h /var --max-depth=3 2>/dev/null | sort -rh | head -10
# Find large files that might be responsible for a full disk
find / -type f -size +500M 2>/dev/null | xargs du -sh | sort -rh
free — Memory Usage
free shows total, used, and available memory. In monitoring and alerting scripts it tells you whether the system is under memory pressure before that pressure causes OOM kills or swap thrashing.
free -h # human-readable (KB, MB, GB)
free -m # in megabytes
# In a script: check available memory and alert if below a threshold
check_memory() {
local threshold_mb="${1:-500}"
local available_mb
# The 'available' column (field 7) is the most accurate measure of free memory
available_mb=$(free -m | awk 'NR==2 {print $7}')
if (( available_mb < threshold_mb )); then
echo "WARN: only ${available_mb}MB available (threshold: ${threshold_mb}MB)" >&2
return 1
fi
echo "OK: ${available_mb}MB memory available"
}
check_memory 500
ps — Process Status
ps lists running processes. It is the tool for finding what is running, who owns it, and how much CPU and memory it is consuming. In scripts, pgrep is usually more convenient than parsing ps output.
ps aux # all processes, BSD format — shows CPU and memory %
ps -ef # all processes, full format — shows PPID and full command
ps aux | grep nginx # find nginx processes
ps -u www-data # processes owned by a specific user
# Custom output columns — choose exactly what you need
ps -eo pid,ppid,user,%cpu,%mem,cmd --sort=-%cpu | head -20
# Find PIDs by name — cleaner than grepping ps output
pgrep nginx # returns matching PIDs, one per line
pgrep -l nginx # with process names
pgrep -a nginx # with full command lines
# Monitor processes continuously without running top interactively
watch -n 2 'ps aux --sort=-%cpu | head -20'
top / htop
top provides a live view of system resource usage. In scripts, the -bn1 flags run it in non-interactive batch mode to get a single snapshot — useful for capturing CPU and load information.
# Non-interactive snapshot — first 20 lines
top -bn1 | head -20
# Extract CPU usage percentage from top output
cpu_idle=$(top -bn1 | grep "Cpu(s)" | awk '{print $8}' | tr -d '%')
cpu_used=$(echo "100 - $cpu_idle" | bc)
echo "CPU used: ${cpu_used}%"
# htop — interactive and more readable, needs installation
# Install: apt install htop / brew install htop
htop
lsof — List Open Files
lsof lists every file a process has open, including network sockets. On Linux, “everything is a file” — so lsof can show open regular files, directories, shared libraries, pipes, and network connections. It is invaluable for debugging “resource busy” errors and finding what is listening on a port.
lsof -p 1234 # all files opened by PID 1234
lsof -u www-data # all files opened by user www-data
lsof /var/log/app.log # which processes currently have this file open
# Network connections
lsof -i # all network connections
lsof -i :80 # which process is bound to port 80
lsof -i :80 -i :443 # check multiple ports at once
lsof -i tcp # TCP connections only
lsof -i -n -P # skip DNS/service name resolution (faster)
# Find the PID of whatever is holding a port — useful before a restart
lsof -ti :8080 # just the PID
kill "$(lsof -ti :8080)" # kill the process occupying port 8080
ss — Socket Statistics (replaces netstat)
ss is the modern replacement for netstat. It is faster, queries the kernel directly, and shows the same information with a similar flag syntax. If you know netstat, ss will feel immediately familiar.
ss -tlnp # TCP listening sockets — no name resolution (-n), with process info (-p)
ss -ulnp # UDP listening sockets
ss -s # summary statistics across all socket types
ss -a # all sockets (both listening and established)
# Find what is listening on a specific port
ss -tlnp | grep :5432 # find PostgreSQL's port
# Show established connections only
ss -tn state established
# Show connections to a specific remote host
ss -tn dst 10.0.0.100
# In a script — check if a service is listening before proceeding
is_port_listening() {
ss -tlnp | grep -q ":${1} "
}
if is_port_listening 80; then
echo "Something is listening on port 80"
fi
systemctl — Service Management
systemctl manages systemd services — the init system used by all modern Linux distributions. In scripts it is used to check service status, start and stop services, and ensure services are enabled to start on boot.
# Check service status
systemctl status nginx
systemctl is-active nginx # prints "active" or "inactive" — good for scripting
systemctl is-enabled nginx # prints "enabled" or "disabled"
# Start / stop / restart
systemctl start nginx
systemctl stop nginx
systemctl restart nginx
systemctl reload nginx # reload configuration without a full restart
# Enable / disable at boot
systemctl enable nginx
systemctl disable nginx
# List services by state
systemctl list-units --type=service --state=running
systemctl list-units --type=service --state=failed
# In a script — ensure a service is running, start it if not
ensure_service_running() {
local service="$1"
if ! systemctl is-active --quiet "$service"; then
echo "Starting $service..."
systemctl start "$service"
sleep 2
if ! systemctl is-active --quiet "$service"; then
echo "ERROR: failed to start $service" >&2
systemctl status "$service" >&2 # show details for debugging
return 1
fi
fi
echo "$service is running"
}
ensure_service_running postgresql
ensure_service_running nginx
journalctl — System Logs
journalctl queries the systemd journal — the centralized log store for all services on a modern Linux system. It is the replacement for tailing individual log files under /var/log.
journalctl -u nginx # all logs for the nginx service
journalctl -u nginx -f # follow in real time (like tail -f)
journalctl -u nginx --since "1 hour ago"
journalctl -u nginx -n 100 # last 100 lines
journalctl -u nginx -p err # errors only
journalctl --since "2025-01-01" --until "2025-01-02"
journalctl -k # kernel messages only
Practical System Health Check Script
Combining these tools into a single health check script gives you a quick, scriptable overview of system status — useful as a cron job, a CI pre-check, or a first step in incident response.
#!/usr/bin/env bash
set -euo pipefail
HOSTNAME=$(hostname)
TIMESTAMP=$(date '+%Y-%m-%d %H:%M:%S')
echo "=== System Health: $HOSTNAME at $TIMESTAMP ==="
echo ""
# CPU load average
LOAD=$(uptime | awk -F'load average:' '{print $2}' | awk '{print $1}' | tr -d ',')
echo "Load average (1m): $LOAD"
# Memory usage
MEM_TOTAL=$(free -m | awk 'NR==2{print $2}')
MEM_USED=$(free -m | awk 'NR==2{print $3}')
MEM_PCT=$(( MEM_USED * 100 / MEM_TOTAL ))
echo "Memory: ${MEM_USED}MB / ${MEM_TOTAL}MB (${MEM_PCT}%)"
# Disk usage on root filesystem
DISK_PCT=$(df -h / | awk 'NR==2{gsub(/%/,""); print $5}')
echo "Disk (/): ${DISK_PCT}%"
# Service status
for svc in nginx postgresql redis; do
if systemctl is-active --quiet "$svc" 2>/dev/null; then
echo "Service $svc: OK"
else
echo "Service $svc: DOWN" >&2
fi
done
What’s Next
The next tutorial covers cron and scheduling — crontab syntax, at, systemd timers, and logging scheduled job output.