Files
chiefgeek a1a17e81a1 Sync all scripts from website downloads — 352 scripts total
Includes updated JS challenge scripts with Claude-User whitelist,
same-site referer bypass, Blackbox-Exporter allowed bot, and all
new exporters, cheat sheets, and automation scripts.
2026-05-25 03:31:08 +02:00

550 lines
22 KiB
Bash

#!/usr/bin/env bash
#########################################################################################
#### sysinfo.sh — One-shot system information dump for Linux servers ####
#### Shows OS, CPU, memory, disk, network, services, load, and uptime at a glance ####
#### No dependencies beyond coreutils and standard Linux tools ####
#### ####
#### Author: Phil Connor ####
#### Contact: contact@mylinux.work ####
#### License: MIT ####
#### Version 1.01 ####
#### ####
#### Usage: ####
#### ./sysinfo.sh ####
#### ./sysinfo.sh --no-color ####
#### ./sysinfo.sh --section disk,network ####
#### ####
#### See --help for all options. ####
#########################################################################################
set -euo pipefail
# ── Defaults ──────────────────────────────────────────────────────────
SECTIONS="${SECTIONS:-all}"
VERBOSE="${VERBOSE:-false}"
COLOR="${COLOR:-auto}"
# ── State ─────────────────────────────────────────────────────────────
SCRIPT_NAME="$(basename "$0")"
readonly SCRIPT_NAME
# ── Colors ────────────────────────────────────────────────────────────
setup_colors() {
if [[ "$COLOR" == "never" ]]; then
RED="" GREEN="" YELLOW="" BLUE="" CYAN="" BOLD="" DIM="" RESET=""
return
fi
if [[ "$COLOR" == "always" ]] || [[ -t 1 ]]; then
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[0;33m'
BLUE='\033[0;34m'
CYAN='\033[0;36m'
BOLD='\033[1m'
DIM='\033[2m'
RESET='\033[0m'
else
RED="" GREEN="" YELLOW="" BLUE="" CYAN="" BOLD="" DIM="" RESET=""
fi
}
# ── Logging ───────────────────────────────────────────────────────────
log() { echo -e "${BLUE}[INFO]${RESET} $*"; }
verbose() { if [[ "$VERBOSE" == "true" ]]; then echo -e "${DIM}[DEBUG]${RESET} $*"; fi; }
# ── Helpers ───────────────────────────────────────────────────────────
section_header() {
echo ""
echo -e " ${BOLD}${CYAN}── $1 ──${RESET}"
echo ""
}
field() {
printf " ${BOLD}%-22s${RESET} %s\n" "$1" "$2"
}
field_color() {
printf " ${BOLD}%-22s${RESET} %b\n" "$1" "$2"
}
human_bytes() {
local bytes="$1"
if [[ "$bytes" -ge 1073741824 ]]; then
awk "BEGIN { printf \"%.1f GiB\", $bytes / 1073741824 }"
elif [[ "$bytes" -ge 1048576 ]]; then
awk "BEGIN { printf \"%.1f MiB\", $bytes / 1048576 }"
elif [[ "$bytes" -ge 1024 ]]; then
awk "BEGIN { printf \"%.1f KiB\", $bytes / 1024 }"
else
echo "${bytes} B"
fi
}
should_show() {
[[ "$SECTIONS" == "all" ]] || [[ ",$SECTIONS," == *",$1,"* ]]
}
# ══════════════════════════════════════════════════════════════════════
# OS INFORMATION
# ══════════════════════════════════════════════════════════════════════
show_os() {
section_header "Operating System"
local hostname_val
hostname_val=$(hostname -f 2>/dev/null || hostname)
field "Hostname:" "$hostname_val"
if [[ -f /etc/os-release ]]; then
local pretty_name version_id
# shellcheck disable=SC1091
pretty_name=$(. /etc/os-release && echo "${PRETTY_NAME:-Unknown}")
# shellcheck disable=SC1091
version_id=$(. /etc/os-release && echo "${VERSION_ID:-}")
field "Distribution:" "$pretty_name"
if [[ -n "$version_id" ]]; then
field "Version:" "$version_id"
fi
elif [[ -f /etc/redhat-release ]]; then
field "Distribution:" "$(cat /etc/redhat-release)"
fi
field "Kernel:" "$(uname -r)"
field "Architecture:" "$(uname -m)"
field "Uptime:" "$(uptime -p 2>/dev/null || uptime | sed 's/.*up //' | sed 's/, [0-9]* user.*//')"
field "Boot time:" "$(who -b 2>/dev/null | awk '{print $3, $4}' || echo "N/A")"
local tz
tz=$(timedatectl 2>/dev/null | grep "Time zone" | awk '{print $3}' || echo "${TZ:-$(cat /etc/timezone 2>/dev/null || echo 'Unknown')}")
field "Timezone:" "$tz"
field "Current time:" "$(date '+%Y-%m-%d %H:%M:%S %Z')"
if command -v sestatus &>/dev/null; then
local selinux_status
selinux_status=$(sestatus 2>/dev/null | grep "SELinux status" | awk '{print $3}' || echo "N/A")
field "SELinux:" "$selinux_status"
fi
}
# ══════════════════════════════════════════════════════════════════════
# CPU INFORMATION
# ══════════════════════════════════════════════════════════════════════
show_cpu() {
section_header "CPU"
if [[ -f /proc/cpuinfo ]]; then
local model cores sockets
model=$(grep -m1 "model name" /proc/cpuinfo | cut -d: -f2 | sed 's/^ //')
cores=$(grep -c "^processor" /proc/cpuinfo || true)
sockets=$(grep "physical id" /proc/cpuinfo | sort -u | wc -l)
field "Model:" "${model:-Unknown}"
field "Logical CPUs:" "$cores"
if [[ "$sockets" -gt 0 ]]; then
field "Sockets:" "$sockets"
fi
else
field "CPUs:" "$(nproc 2>/dev/null || echo 'Unknown')"
fi
# Load averages
if [[ -f /proc/loadavg ]]; then
local load
load=$(cut -d' ' -f1-3 /proc/loadavg)
field "Load average:" "$load"
fi
# CPU usage snapshot
if command -v mpstat &>/dev/null; then
local idle
idle=$(mpstat 1 1 2>/dev/null | tail -1 | awk '{print $NF}')
if [[ -n "$idle" ]]; then
local used
used=$(awk "BEGIN { printf \"%.1f\", 100 - $idle }")
field "CPU usage:" "${used}%"
fi
fi
}
# ══════════════════════════════════════════════════════════════════════
# MEMORY INFORMATION
# ══════════════════════════════════════════════════════════════════════
show_memory() {
section_header "Memory"
if [[ -f /proc/meminfo ]]; then
local total_kb free_kb avail_kb buffers_kb cached_kb swap_total_kb swap_free_kb
total_kb=$(awk '/^MemTotal:/ {print $2}' /proc/meminfo)
free_kb=$(awk '/^MemFree:/ {print $2}' /proc/meminfo)
avail_kb=$(awk '/^MemAvailable:/ {print $2}' /proc/meminfo)
buffers_kb=$(awk '/^Buffers:/ {print $2}' /proc/meminfo)
cached_kb=$(awk '/^Cached:/ {print $2}' /proc/meminfo)
swap_total_kb=$(awk '/^SwapTotal:/ {print $2}' /proc/meminfo)
swap_free_kb=$(awk '/^SwapFree:/ {print $2}' /proc/meminfo)
local total_b used_b avail_b
total_b=$((total_kb * 1024))
avail_b=$((avail_kb * 1024))
used_b=$(( (total_kb - free_kb - buffers_kb - cached_kb) * 1024 ))
local pct_used
pct_used=$(awk "BEGIN { printf \"%.0f\", ($total_kb - $avail_kb) * 100 / $total_kb }")
local color="$GREEN"
if [[ "$pct_used" -ge 90 ]]; then
color="$RED"
elif [[ "$pct_used" -ge 75 ]]; then
color="$YELLOW"
fi
field "Total:" "$(human_bytes "$total_b")"
field_color "Used:" "${color}$(human_bytes "$used_b") (${pct_used}%)${RESET}"
field "Available:" "$(human_bytes "$avail_b")"
if [[ "$swap_total_kb" -gt 0 ]]; then
local swap_used_kb swap_pct
swap_used_kb=$((swap_total_kb - swap_free_kb))
swap_pct=$(awk "BEGIN { printf \"%.0f\", $swap_used_kb * 100 / $swap_total_kb }")
field "Swap:" "$(human_bytes $((swap_total_kb * 1024))) total, $(human_bytes $((swap_used_kb * 1024))) used (${swap_pct}%)"
else
field "Swap:" "None"
fi
else
free -h 2>/dev/null || echo " Memory info unavailable"
fi
}
# ══════════════════════════════════════════════════════════════════════
# DISK INFORMATION
# ══════════════════════════════════════════════════════════════════════
show_disk() {
section_header "Disk"
printf " ${BOLD}%-20s %8s %8s %8s %6s${RESET}\n" "FILESYSTEM" "SIZE" "USED" "AVAIL" "USE%"
printf " %s\n" "$(printf '%.0s─' {1..58})"
df -h --output=target,size,used,avail,pcent -x tmpfs -x devtmpfs -x overlay 2>/dev/null | tail -n +2 | sort | while IFS= read -r line; do
local mount size used avail pct
mount=$(echo "$line" | awk '{print $1}')
size=$(echo "$line" | awk '{print $2}')
used=$(echo "$line" | awk '{print $3}')
avail=$(echo "$line" | awk '{print $4}')
pct=$(echo "$line" | awk '{print $5}' | tr -d '%')
local color="$GREEN"
if [[ -n "$pct" ]]; then
if [[ "$pct" -ge 90 ]]; then
color="$RED"
elif [[ "$pct" -ge 75 ]]; then
color="$YELLOW"
fi
fi
printf " %-20s %8s %8s %8s %b%5s%%%b\n" "$mount" "$size" "$used" "$avail" "$color" "${pct:-?}" "$RESET"
done
# Inode usage for root
echo ""
local inode_pct
inode_pct=$(df -i / 2>/dev/null | tail -1 | awk '{print $5}' | tr -d '%')
if [[ -n "$inode_pct" ]]; then
local icolor="$GREEN"
if [[ "$inode_pct" -ge 90 ]]; then icolor="$RED"
elif [[ "$inode_pct" -ge 75 ]]; then icolor="$YELLOW"; fi
field_color "Inode usage (/):" "${icolor}${inode_pct}%${RESET}"
fi
}
# ══════════════════════════════════════════════════════════════════════
# NETWORK INFORMATION
# ══════════════════════════════════════════════════════════════════════
show_network() {
section_header "Network"
# Interfaces with IPs
printf " ${BOLD}%-16s %-18s %-16s %s${RESET}\n" "INTERFACE" "IPv4" "STATE" "MAC"
printf " %s\n" "$(printf '%.0s─' {1..65})"
if command -v ip &>/dev/null; then
ip -o addr show 2>/dev/null | grep "inet " | while IFS= read -r line; do
local iface addr state mac
iface=$(echo "$line" | awk '{print $2}')
addr=$(echo "$line" | awk '{print $4}')
[[ "$iface" == "lo" ]] && continue
state=$(ip link show "$iface" 2>/dev/null | grep -o "state [A-Z]*" | awk '{print $2}')
mac=$(ip link show "$iface" 2>/dev/null | grep "link/ether" | awk '{print $2}')
printf " %-16s %-18s %-16s %s\n" "$iface" "$addr" "${state:-UNKNOWN}" "${mac:--}"
done
fi
# Default gateway
local gw
gw=$(ip route show default 2>/dev/null | awk '{print $3, "via", $5}' | head -1)
if [[ -n "$gw" ]]; then
echo ""
field "Default gateway:" "$gw"
fi
# DNS servers
local dns
dns=$(grep "^nameserver" /etc/resolv.conf 2>/dev/null | awk '{print $2}' | tr '\n' ' ')
if [[ -n "$dns" ]]; then
field "DNS servers:" "$dns"
fi
# Listening ports (top 15)
if command -v ss &>/dev/null; then
echo ""
printf " ${BOLD}%-8s %-24s %s${RESET}\n" "PROTO" "LISTEN" "PROCESS"
printf " %s\n" "$(printf '%.0s─' {1..55})"
ss -tlnp 2>/dev/null | tail -n +2 | head -15 | while IFS= read -r line; do
local proto addr proc_info
proto="tcp"
addr=$(echo "$line" | awk '{print $4}')
proc_info=$(echo "$line" | grep -oP 'users:\(\("\K[^"]+' || echo "-")
printf " %-8s %-24s %s\n" "$proto" "$addr" "$proc_info"
done
local total_listeners
total_listeners=$(ss -tlnp 2>/dev/null | tail -n +2 | wc -l)
if [[ "$total_listeners" -gt 15 ]]; then
echo -e " ${DIM}... and $((total_listeners - 15)) more${RESET}"
fi
fi
}
# ══════════════════════════════════════════════════════════════════════
# SERVICES
# ══════════════════════════════════════════════════════════════════════
show_services() {
section_header "Services"
if command -v systemctl &>/dev/null; then
# Failed services
local failed_count
failed_count=$(systemctl --no-legend --state=failed 2>/dev/null | wc -l)
if [[ "$failed_count" -gt 0 ]]; then
field_color "Failed services:" "${RED}${failed_count}${RESET}"
echo ""
systemctl --no-legend --state=failed 2>/dev/null | while IFS= read -r line; do
local unit
unit=$(echo "$line" | awk '{print $2}')
printf " ${RED}${RESET} %s\n" "$unit"
done
echo ""
else
field_color "Failed services:" "${GREEN}0${RESET}"
fi
# Running service count
local running_count
running_count=$(systemctl --no-legend --type=service --state=running 2>/dev/null | wc -l)
field "Running services:" "$running_count"
# Enabled timers
local timer_count
timer_count=$(systemctl --no-legend --type=timer --state=active 2>/dev/null | wc -l)
field "Active timers:" "$timer_count"
else
log "systemd not available"
fi
}
# ══════════════════════════════════════════════════════════════════════
# SECURITY
# ══════════════════════════════════════════════════════════════════════
show_security() {
section_header "Security"
# Pending updates
if command -v apt-get &>/dev/null; then
local upgradable
local apt_output
apt_output=$(apt list --upgradable 2>/dev/null || true)
upgradable=$(echo "$apt_output" | grep -c "upgradable" || true)
field "Pending updates:" "$upgradable"
local security_updates
security_updates=$(echo "$apt_output" | grep -c "\-security" || true)
if [[ "$security_updates" -gt 0 ]]; then
field_color "Security updates:" "${RED}${security_updates}${RESET}"
fi
elif command -v yum &>/dev/null; then
local yum_updates
yum_updates=$(yum check-update --quiet 2>/dev/null | grep -cE "^\S" || true)
field "Pending updates:" "$yum_updates"
elif command -v dnf &>/dev/null; then
local dnf_updates
dnf_updates=$(dnf check-update --quiet 2>/dev/null | grep -cE "^\S" || true)
field "Pending updates:" "$dnf_updates"
fi
# Logged in users
local user_count
user_count=$(who 2>/dev/null | wc -l)
field "Logged in users:" "$user_count"
if [[ "$user_count" -gt 0 ]]; then
who 2>/dev/null | awk '{printf " %s from %s since %s %s\n", $1, $5, $3, $4}'
fi
# Reboot required
if [[ -f /var/run/reboot-required ]]; then
echo ""
field_color "Reboot required:" "${YELLOW}YES${RESET}"
fi
# Last 5 failed login attempts
if command -v lastb &>/dev/null && [[ -r /var/log/btmp ]]; then
local failed_logins
failed_logins=$(lastb -n 5 2>/dev/null | head -5 | grep -c "" || true)
if [[ "$failed_logins" -gt 0 ]]; then
echo ""
field "Recent failed logins:" ""
lastb -n 5 2>/dev/null | head -5 | while IFS= read -r line; do
[[ -z "$line" ]] && continue
printf " %s\n" "$line"
done
fi
fi
}
# ══════════════════════════════════════════════════════════════════════
# DOCKER / CONTAINERS
# ══════════════════════════════════════════════════════════════════════
show_containers() {
if ! command -v docker &>/dev/null && ! command -v podman &>/dev/null; then
return
fi
section_header "Containers"
local runtime="docker"
if ! command -v docker &>/dev/null; then
runtime="podman"
fi
if ! "$runtime" info &>/dev/null 2>&1; then
field "Status:" "${runtime} not accessible (permission denied or not running)"
return
fi
local running stopped total
running=$("$runtime" ps -q 2>/dev/null | wc -l)
total=$("$runtime" ps -aq 2>/dev/null | wc -l)
stopped=$((total - running))
field "Runtime:" "$runtime"
field "Running:" "$running"
field "Stopped:" "$stopped"
# Disk usage
if [[ "$runtime" == "docker" ]]; then
local docker_disk
docker_disk=$(docker system df --format "{{.Size}}" 2>/dev/null | head -1)
if [[ -n "$docker_disk" ]]; then
field "Image disk:" "$docker_disk"
fi
fi
}
# ══════════════════════════════════════════════════════════════════════
# USAGE
# ══════════════════════════════════════════════════════════════════════
usage() {
cat <<EOF
${SCRIPT_NAME} — One-shot system information dump
USAGE:
${SCRIPT_NAME} [OPTIONS]
OPTIONS:
--section SECTIONS Comma-separated sections to show (default: all)
Available: os, cpu, memory, disk, network, services,
security, containers
--verbose Enable debug output
--no-color Disable colored output
--help Show this help
ENVIRONMENT VARIABLES:
SECTIONS Sections to display (default: all)
COLOR Color mode: auto, always, never (default: auto)
EXAMPLES:
# Full system info
./sysinfo.sh
# Disk and network only
./sysinfo.sh --section disk,network
# Pipe-friendly output
./sysinfo.sh --no-color | tee /tmp/sysinfo-\$(hostname).txt
EOF
}
# ══════════════════════════════════════════════════════════════════════
# ARGUMENT PARSING
# ══════════════════════════════════════════════════════════════════════
parse_args() {
while [[ $# -gt 0 ]]; do
case "$1" in
--section)
SECTIONS="$2"; shift 2 ;;
--verbose)
VERBOSE="true"; shift ;;
--no-color)
COLOR="never"; shift ;;
--help|-h)
setup_colors
usage
exit 0 ;;
*)
echo "Unknown option: $1" >&2
echo "Run ${SCRIPT_NAME} --help for usage" >&2
exit 1 ;;
esac
done
}
# ══════════════════════════════════════════════════════════════════════
# MAIN
# ══════════════════════════════════════════════════════════════════════
main() {
parse_args "$@"
setup_colors
echo ""
echo -e "${BOLD}System Information — $(hostname -f 2>/dev/null || hostname)${RESET}"
echo -e "${DIM}$(date '+%Y-%m-%d %H:%M:%S %Z')${RESET}"
should_show "os" && show_os
should_show "cpu" && show_cpu
should_show "memory" && show_memory
should_show "disk" && show_disk
should_show "network" && show_network
should_show "services" && show_services
should_show "security" && show_security
should_show "containers" && show_containers
echo ""
}
main "$@"