Files
linux-scripts/process-top.sh
T
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

364 lines
15 KiB
Bash

#!/usr/bin/env bash
#########################################################################################
#### process-top.sh — Show top CPU/memory consumers, zombies, and long-running procs ####
#### Provides a quick snapshot of process health and resource usage ####
#### ####
#### Author: Phil Connor ####
#### Contact: contact@mylinux.work ####
#### License: MIT ####
#### Version 1.00 ####
#### ####
#### Usage: ####
#### ./process-top.sh ####
#### ./process-top.sh --top 20 --section cpu,zombies ####
#### ./process-top.sh --min-runtime 48 ####
#### ####
#### See --help for all options. ####
#########################################################################################
set -euo pipefail
# ── Defaults ──────────────────────────────────────────────────────────
SECTIONS="${SECTIONS:-all}"
VERBOSE="${VERBOSE:-false}"
COLOR="${COLOR:-auto}"
TOP_N="${TOP_N:-10}"
MIN_RUNTIME="${MIN_RUNTIME:-24}"
# ── State ─────────────────────────────────────────────────────────────
SCRIPT_NAME="$(basename "$0")"
readonly SCRIPT_NAME
# ── Colors ────────────────────────────────────────────────────────────
setup_colors() {
if [[ "$COLOR" == "never" ]]; then
RED="" GREEN="" YELLOW="" CYAN="" BOLD="" DIM="" RESET=""
return
fi
if [[ "$COLOR" == "always" ]] || [[ -t 1 ]]; then
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[0;33m'
CYAN='\033[0;36m'
BOLD='\033[1m'
DIM='\033[2m'
RESET='\033[0m'
else
RED="" GREEN="" YELLOW="" CYAN="" BOLD="" DIM="" RESET=""
fi
}
# ── Logging ───────────────────────────────────────────────────────────
log() { echo -e "${CYAN}[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"
}
should_show() {
[[ "$SECTIONS" == "all" ]] || [[ ",$SECTIONS," == *",$1,"* ]]
}
value_color() {
local value="$1"
local int_val
int_val=$(printf "%.0f" "$value" 2>/dev/null || echo "0")
if [[ "$int_val" -ge 80 ]]; then
echo "$YELLOW"
else
echo "$GREEN"
fi
}
# ══════════════════════════════════════════════════════════════════════
# TOP CPU
# ══════════════════════════════════════════════════════════════════════
show_cpu() {
section_header "Top ${TOP_N} by CPU Usage"
printf " ${BOLD}%-8s %-12s %6s %6s %10s %s${RESET}\n" "PID" "USER" "CPU%" "MEM%" "TIME" "COMMAND"
printf " %s\n" "$(printf '%.0s─' {1..72})"
ps -eo pid,user,pcpu,pmem,etime,comm --sort=-pcpu --no-headers 2>/dev/null | head -n "$TOP_N" | while IFS= read -r line; do
local pid user cpu mem etime comm
pid=$(echo "$line" | awk '{print $1}')
user=$(echo "$line" | awk '{print $2}')
cpu=$(echo "$line" | awk '{print $3}')
mem=$(echo "$line" | awk '{print $4}')
etime=$(echo "$line" | awk '{print $5}')
comm=$(echo "$line" | awk '{print $6}')
local cc mc
cc=$(value_color "$cpu")
mc=$(value_color "$mem")
printf " %-8s %-12s %b%5s%%%b %b%5s%%%b %10s %s\n" \
"$pid" "$user" "$cc" "$cpu" "$RESET" "$mc" "$mem" "$RESET" "$etime" "$comm"
done
}
# ══════════════════════════════════════════════════════════════════════
# TOP MEMORY
# ══════════════════════════════════════════════════════════════════════
show_memory() {
section_header "Top ${TOP_N} by Memory Usage"
printf " ${BOLD}%-8s %-12s %6s %6s %10s %s${RESET}\n" "PID" "USER" "CPU%" "MEM%" "TIME" "COMMAND"
printf " %s\n" "$(printf '%.0s─' {1..72})"
ps -eo pid,user,pcpu,pmem,etime,comm --sort=-pmem --no-headers 2>/dev/null | head -n "$TOP_N" | while IFS= read -r line; do
local pid user cpu mem etime comm
pid=$(echo "$line" | awk '{print $1}')
user=$(echo "$line" | awk '{print $2}')
cpu=$(echo "$line" | awk '{print $3}')
mem=$(echo "$line" | awk '{print $4}')
etime=$(echo "$line" | awk '{print $5}')
comm=$(echo "$line" | awk '{print $6}')
local cc mc
cc=$(value_color "$cpu")
mc=$(value_color "$mem")
printf " %-8s %-12s %b%5s%%%b %b%5s%%%b %10s %s\n" \
"$pid" "$user" "$cc" "$cpu" "$RESET" "$mc" "$mem" "$RESET" "$etime" "$comm"
done
}
# ══════════════════════════════════════════════════════════════════════
# ZOMBIES
# ══════════════════════════════════════════════════════════════════════
show_zombies() {
section_header "Zombie Processes"
local zombie_list
zombie_list=$(ps -eo pid,ppid,user,stat,comm --no-headers 2>/dev/null | awk '$4 ~ /^Z/')
if [[ -z "$zombie_list" ]]; then
echo -e " ${GREEN}No zombie processes found${RESET}"
return
fi
printf " ${BOLD}%-8s %-8s %-12s %-6s %s${RESET}\n" "PID" "PPID" "USER" "STAT" "COMMAND"
printf " %s\n" "$(printf '%.0s─' {1..55})"
echo "$zombie_list" | while IFS= read -r line; do
local pid ppid user stat comm
pid=$(echo "$line" | awk '{print $1}')
ppid=$(echo "$line" | awk '{print $2}')
user=$(echo "$line" | awk '{print $3}')
stat=$(echo "$line" | awk '{print $4}')
comm=$(echo "$line" | awk '{print $5}')
printf " ${RED}%-8s${RESET} %-8s %-12s ${RED}%-6s${RESET} %s\n" \
"$pid" "$ppid" "$user" "$stat" "$comm"
done
}
# ══════════════════════════════════════════════════════════════════════
# LONG-RUNNING
# ══════════════════════════════════════════════════════════════════════
etime_to_hours() {
local etime="$1"
local days=0 hours=0 mins=0
if [[ "$etime" == *-* ]]; then
days=${etime%%-*}
etime=${etime#*-}
fi
local parts
IFS=: read -ra parts <<< "$etime"
local num_parts=${#parts[@]}
if [[ "$num_parts" -eq 3 ]]; then
hours=${parts[0]}
mins=${parts[1]}
elif [[ "$num_parts" -eq 2 ]]; then
hours=${parts[0]}
mins=${parts[1]}
elif [[ "$num_parts" -eq 1 ]]; then
mins=${parts[0]}
fi
# Remove leading zeros
days=$((10#$days))
hours=$((10#$hours))
mins=$((10#$mins))
echo $(( days * 24 + hours + (mins > 30 ? 1 : 0) ))
}
show_long_running() {
section_header "Long-Running Processes (> ${MIN_RUNTIME}h)"
printf " ${BOLD}%-8s %-12s %10s %6s %s${RESET}\n" "PID" "USER" "ELAPSED" "MEM%" "COMMAND"
printf " %s\n" "$(printf '%.0s─' {1..62})"
local found=0
ps -eo pid,user,etime,pmem,comm --no-headers 2>/dev/null | while IFS= read -r line; do
local pid user etime mem comm
pid=$(echo "$line" | awk '{print $1}')
user=$(echo "$line" | awk '{print $2}')
etime=$(echo "$line" | awk '{print $3}')
mem=$(echo "$line" | awk '{print $4}')
comm=$(echo "$line" | awk '{print $5}')
local run_hours
run_hours=$(etime_to_hours "$etime")
if [[ "$run_hours" -ge "$MIN_RUNTIME" ]]; then
printf " %-8s %-12s %10s %5s%% %s\n" "$pid" "$user" "$etime" "$mem" "$comm"
found=1
fi
done
if [[ "$found" -eq 0 ]]; then
echo -e " ${GREEN}No processes running longer than ${MIN_RUNTIME} hours${RESET}"
fi
}
# ══════════════════════════════════════════════════════════════════════
# SUMMARY
# ══════════════════════════════════════════════════════════════════════
print_summary() {
echo ""
echo -e " ${BOLD}══════════════════════════════════════════${RESET}"
echo -e " ${BOLD}Process Summary${RESET}"
echo -e " ${BOLD}══════════════════════════════════════════${RESET}"
local total_procs zombie_count long_count
total_procs=$(ps -e --no-headers 2>/dev/null | wc -l)
zombie_count=$(ps -eo stat --no-headers 2>/dev/null | awk '/^Z/{c++} END{print c+0}')
long_count=$(ps -eo etime --no-headers 2>/dev/null | while IFS= read -r etime; do
etime=$(echo "$etime" | xargs)
local h
h=$(etime_to_hours "$etime")
if [[ "$h" -ge "$MIN_RUNTIME" ]]; then
echo "1"
fi
done | wc -l)
field "Total processes:" "$total_procs"
if [[ "$zombie_count" -gt 0 ]]; then
field_color "Zombie processes:" "${RED}${zombie_count}${RESET}"
else
field_color "Zombie processes:" "${GREEN}0${RESET}"
fi
field "Long-running (>${MIN_RUNTIME}h):" "$long_count"
echo ""
}
# ══════════════════════════════════════════════════════════════════════
# USAGE
# ══════════════════════════════════════════════════════════════════════
usage() {
cat <<EOF
${SCRIPT_NAME} — Show top CPU/memory consumers, zombies, and long-running processes
USAGE:
${SCRIPT_NAME} [OPTIONS]
OPTIONS:
--top N Number of processes to show (default: ${TOP_N})
--min-runtime HOURS Long-running threshold in hours (default: ${MIN_RUNTIME})
--section SECTIONS Comma-separated sections to show (default: all)
Available: cpu, memory, zombies, long-running
--verbose Enable debug output
--no-color Disable colored output
--help Show this help
ENVIRONMENT VARIABLES:
TOP_N Number of top processes (default: 10)
MIN_RUNTIME Long-running threshold in hours (default: 24)
SECTIONS Sections to display (default: all)
COLOR Color mode: auto, always, never (default: auto)
EXAMPLES:
# Full process overview
./process-top.sh
# Top 20 CPU consumers
./process-top.sh --top 20 --section cpu
# Find zombies and long-running processes
./process-top.sh --section zombies,long-running
# Processes running longer than 48 hours
./process-top.sh --min-runtime 48 --section long-running
EOF
}
# ══════════════════════════════════════════════════════════════════════
# ARGUMENT PARSING
# ══════════════════════════════════════════════════════════════════════
parse_args() {
while [[ $# -gt 0 ]]; do
case "$1" in
--top)
TOP_N="$2"; shift 2 ;;
--min-runtime)
MIN_RUNTIME="$2"; shift 2 ;;
--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}Process Monitor — $(hostname -f 2>/dev/null || hostname)${RESET}"
echo -e "${DIM}$(date '+%Y-%m-%d %H:%M:%S %Z')${RESET}"
should_show "cpu" && show_cpu
should_show "memory" && show_memory
should_show "zombies" && show_zombies
should_show "long-running" && show_long_running
print_summary
}
main "$@"