Add systemState.sh

This commit is contained in:
2026-02-18 09:54:44 +03:00
commit 73483ff7db

479
systemState.sh Normal file
View File

@ -0,0 +1,479 @@
#!/bin/bash
# Цвета и стили
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
PURPLE='\033[0;35m'
CYAN='\033[0;36m'
WHITE='\033[1;37m'
NC='\033[0m' # No Color
BOLD='\033[1m'
# Функция для создания прогресс-бара
create_progress_bar() {
local used_percent=$1
local bar_length=50
local filled=$(echo "scale=0; $used_percent * $bar_length / 100" | bc 2>/dev/null || echo "0")
local empty=$((bar_length - filled))
# Ограничиваем значения
if [ $filled -gt $bar_length ]; then filled=$bar_length; fi
if [ $empty -lt 0 ]; then empty=0; fi
printf "["
printf "${RED}%*s${NC}" $filled | tr ' ' '='
printf "${GREEN}%*s${NC}" $empty | tr ' ' '='
printf "]"
}
# ОДНОКРАТНЫЙ СБОР ВСЕХ ДАННЫХ
echo -e "${YELLOW}Сбор информации о системе...${NC}"
# Uptime
uptime_full=$(uptime)
# Информация о памяти (один раз)
mem_info=$(free -b | grep "^Mem:")
swap_info=$(free -b | grep "^Swap:")
# Информация о дисках (один раз)
disk_info=$(df -h 2>/dev/null)
# Информация о процессах (один раз)
process_data=$(ps aux --no-headers 2>/dev/null)
# Информация о сети и ошибках (один раз)
if command -v ss &> /dev/null; then
port_info=$(ss -tulpn 2>/dev/null)
fi
net_dev_info=$(cat /proc/net/dev 2>/dev/null)
# Информация о нагрузке CPU (iowait и другие метрики)
if command -v mpstat &> /dev/null; then
mpstat_info=$(mpstat 1 1 2>/dev/null | tail -1)
else
# Fallback на чтение из /proc/stat
cpu_stats=$(cat /proc/stat 2>/dev/null | grep "^cpu " | head -1)
fi
# Информация о дисковом вводе-выводе
if command -v iostat &> /dev/null; then
iostat_info=$(iostat -x 1 2 2>/dev/null | grep -A 100 "Device" | tail -n +3)
fi
# Информация о системных ограничениях
if [ -f /proc/sys/fs/file-nr ]; then
file_handles=$(cat /proc/sys/fs/file-nr 2>/dev/null)
fi
# Информация о загрузке системы
load_avg=$(echo "$uptime_full" | awk -F'load average:' '{print $2}' | sed 's/^[ \t]*//')
load_1min=$(echo $load_avg | awk -F',' '{print $1}' | tr -d ' ')
# Количество CPU
cpu_count=$(nproc)
echo -e "${GREEN}✓ Данные собраны${NC}\n"
# Парсим uptime
if [[ $uptime_full =~ up\ (.*),\ *([0-9]+)\ users?,\ *load\ average:\ (.*) ]]; then
current_time=$(echo "$uptime_full" | awk '{print $1}')
uptime_part="${BASH_REMATCH[1]}"
users="${BASH_REMATCH[2]}"
elif [[ $uptime_full =~ up\ (.*),\ *load\ average:\ (.*) ]]; then
current_time=$(echo "$uptime_full" | awk '{print $1}')
uptime_part=$(echo "$uptime_full" | sed -n 's/.*up \(.*\),.*load.*/\1/p')
users=$(who | wc -l)
else
current_time=$(date +"%H:%M:%S")
uptime_part=$(echo "$uptime_full" | awk -F'up ' '{print $2}' | awk -F',' '{print $1}')
users=$(who | wc -l)
fi
uptime_part=$(echo "$uptime_part" | sed 's/^[ \t]*//;s/[ \t]*$//')
# Парсим память
mem_total=$(echo "$mem_info" | awk '{print $2}')
mem_used=$(echo "$mem_info" | awk '{print $3}')
mem_available=$(echo "$mem_info" | awk '{print $7}')
mem_percent=$(echo "scale=0; $mem_used * 100 / $mem_total" | bc 2>/dev/null || echo "0")
swap_total=$(echo "$swap_info" | awk '{print $2}')
swap_used=$(echo "$swap_info" | awk '{print $3}')
if [ $swap_total -gt 0 ]; then
swap_percent=$(echo "scale=0; $swap_used * 100 / $swap_total" | bc 2>/dev/null || echo "0")
else
swap_percent=0
fi
# Форматируем для вывода
mem_total_h=$(numfmt --to=iec --suffix=B $mem_total 2>/dev/null || echo "${mem_total}KB")
mem_used_h=$(numfmt --to=iec --suffix=B $mem_used 2>/dev/null || echo "${mem_used}KB")
mem_available_h=$(numfmt --to=iec --suffix=B $mem_available 2>/dev/null || echo "${mem_available}KB")
if [ $swap_total -gt 0 ]; then
swap_total_h=$(numfmt --to=iec --suffix=B $swap_total 2>/dev/null || echo "${swap_total}KB")
swap_used_h=$(numfmt --to=iec --suffix=B $swap_used 2>/dev/null || echo "${swap_used}KB")
swap_free_h=$(numfmt --to=iec --suffix=B $((swap_total - swap_used)) 2>/dev/null || echo "$((swap_total - swap_used))KB")
fi
# Анализируем iowait из mpstat или /proc/stat
iowait_value=0
if [ -n "$mpstat_info" ]; then
iowait_value=$(echo "$mpstat_info" | awk '{print $6}')
elif [ -n "$cpu_stats" ]; then
# Парсим /proc/stat для получения iowait
user=$(echo $cpu_stats | awk '{print $2}')
nice=$(echo $cpu_stats | awk '{print $3}')
system=$(echo $cpu_stats | awk '{print $4}')
idle=$(echo $cpu_stats | awk '{print $5}')
iowait=$(echo $cpu_stats | awk '{print $6}')
irq=$(echo $cpu_stats | awk '{print $7}')
softirq=$(echo $cpu_stats | awk '{print $8}')
total=$((user + nice + system + idle + iowait + irq + softirq))
iowait_value=$(echo "scale=2; $iowait * 100 / $total" | bc 2>/dev/null || echo "0")
fi
# Анализируем ошибки на интерфейсах
declare -A interface_errors
if [ -n "$net_dev_info" ]; then
while read line; do
if [[ $line =~ ^[[:space:]]*([a-zA-Z0-9]+): ]]; then
interface="${BASH_REMATCH[1]}"
# Пропускаем loopback
if [ "$interface" = "lo" ]; then continue; fi
# Получаем ошибки при приеме и передаче
receive_errors=$(echo $line | awk '{print $4}')
transmit_errors=$(echo $line | awk '{print $12}')
receive_drops=$(echo $line | awk '{print $5}')
transmit_drops=$(echo $line | awk '{print $13}')
total_errors=$((receive_errors + transmit_errors + receive_drops + transmit_drops))
if [ $total_errors -gt 0 ]; then
interface_errors[$interface]="$receive_errors,$transmit_errors,$receive_drops,$transmit_drops"
fi
fi
done <<< "$net_dev_info"
fi
# Анализируем дисковый ввод-вывод
high_iowait_disks=()
if [ -n "$iostat_info" ]; then
while read line; do
if [[ $line =~ ^([a-zA-Z0-9]+) ]]; then
device="${BASH_REMATCH[1]}"
util=$(echo $line | awk '{print $NF}')
if (( $(echo "$util > 80" | bc -l 2>/dev/null) )); then
high_iowait_disks+=("$device ($util%)")
fi
fi
done <<< "$iostat_info"
fi
# Анализируем файловые дескрипторы
file_handles_warning=0
if [ -n "$file_handles" ]; then
allocated=$(echo $file_handles | awk '{print $1}')
maximum=$(echo $file_handles | awk '{print $3}')
if [ $maximum -gt 0 ]; then
file_handle_percent=$(echo "scale=0; $allocated * 100 / $maximum" | bc 2>/dev/null || echo "0")
if [ $file_handle_percent -gt 80 ]; then
file_handles_warning=1
fi
fi
fi
# ВЫВОД ИНФОРМАЦИИ
echo -e "${BOLD}${BLUE}╔════════════════════════════════════════════════════════════╗${NC}"
echo -e "${BOLD}${BLUE}║ SYSTEM INFO ║${NC}"
echo -e "${BOLD}${BLUE}╚════════════════════════════════════════════════════════════╝${NC}"
# Имя сервера
echo -e "${BOLD}Server Name:${GREEN} $(hostname)${NC}"
# OS
if [ -f /etc/os-release ]; then
. /etc/os-release
echo -e "${BOLD}OS:${NC} $PRETTY_NAME"
else
echo -e "${BOLD}OS:${RED} Unknown${NC}"
fi
# Внутренние IP
echo -e "${BOLD}IP:${NC}"
ip -4 addr show 2>/dev/null | grep -oP '(?<=inet\s)\d+(\.\d+){3}' | while read ip; do
echo " - $ip"
done
# Uptime информация
echo -e "${BOLD}Time:${NC} $current_time"
echo -e "${BOLD}Uptime:${NC} $uptime_part"
echo -e "${BOLD}Logged in users:${NC} $users"
# CPU RAM SWAP
echo -e "${BOLD}CPU count:${NC} $cpu_count"
echo -e "${BOLD}RAM total:${NC} $$mem_total_h"
if [ $swap_total -gt 0 ]; then
echo -e "${BOLD}RAM total:${NC} $$swap_total"
fi
echo -e "${BOLD}${BLUE}╔════════════════════════════════════════════════════════════╗${NC}"
echo -e "${BOLD}${BLUE}║ SYSTEM STATE ║${NC}"
echo -e "${BOLD}${BLUE}╚════════════════════════════════════════════════════════════╝${NC}"
# Load average
if (( $(echo "$load_1min > $cpu_count" | bc -l 2>/dev/null) )); then
echo -e "${BOLD}Load:${NC} ${RED}$load_avg${NC}"
else
echo -e "${BOLD}Load:${NC} $load_avg"
fi
# IOWait
if (( $(echo "$iowait_value > 10" | bc -l 2>/dev/null) )); then
echo -e "${BOLD}IOWait:${NC} ${RED}${iowait_value}%${NC} (high)"
else
echo -e "${BOLD}IOWait:${NC} ${iowait_value}%"
fi
echo ""
# Оперативная память и Swap
echo -e "${BOLD}${BLUE}═══════════════════════════ RAM/SWAP ════════════════════════${NC}"
echo -e "${CYAN}RAM${NC}: ${WHITE}$mem_total_h${NC} total, ${WHITE}$mem_used_h${NC} used, ${WHITE}$mem_available_h${NC} free"
create_progress_bar $mem_percent
echo ""
# Swap
if [ $swap_total -gt 0 ]; then
echo -e "${CYAN}SWAP${NC}: ${WHITE}$swap_total_h${NC} total, ${WHITE}$swap_used_h${NC} used, ${WHITE}$swap_free_h${NC} free"
create_progress_bar $swap_percent
echo ""
fi
echo ""
# Дисковое пространство
echo -e "${BOLD}${BLUE}═════════════════════════════ DISK ═══════════════════════════${NC}"
# Исключенные типы файловых систем
excluded_types=(
"devtmpfs"
"ecryptfs"
"squashfs"
"tmpfs"
"udev"
"overlay"
)
# Формируем строку исключений для grep
exclude_pattern=$(printf "|%s" "${excluded_types[@]}")
exclude_pattern=${exclude_pattern:1}
# Массив для хранения информации о дисках для резюме
declare -A disk_usage
echo "$disk_info" | tail -n+2 | grep -vE "^($exclude_pattern)" | while read line; do
filesystem=$(echo $line | awk '{print $1}')
size=$(echo $line | awk '{print $2}')
used=$(echo $line | awk '{print $3}')
available=$(echo $line | awk '{print $4}')
mountpoint=$(echo $line | awk '{print $6}')
used_percent=$(echo $line | awk '{print $5}' | sed 's/%//')
# Сохраняем для резюме
disk_usage["$mountpoint"]=$used_percent
echo -e "${CYAN}$mountpoint${NC} (${YELLOW}$filesystem${NC}): ${WHITE}$size${NC} total, ${WHITE}$used${NC} used, ${WHITE}$available${NC} free"
create_progress_bar $used_percent
echo ""
done
echo ""
# Количество открытых портов
echo -e "${BOLD}${BLUE}═════════════════════════ LISTEN PORTS ═══════════════════════${NC}"
if [ -n "$port_info" ]; then
port_count=$(echo "$port_info" | grep -v "127.0.0.1" | grep -v "::1" | tail -n+2 | wc -l)
echo -e "${BOLD}Всего открытых портов (слушающих):${NC} $port_count"
echo ""
echo -e "${CYAN}Список портов и процессов:${NC}"
echo "$port_info" | grep -v "127.0.0.1" | grep -v "::1" | tail -n+2 | while read line; do
protocol=$(echo $line | awk '{print $1}')
address=$(echo $line | awk '{print $5}')
port=$(echo $address | grep -oP ':\K\d+$')
process=$(echo $line | grep -oP 'users:\(\("\K[^"]+' || echo "unknown")
if [ -n "$port" ]; then
printf " ${YELLOW}%-5s${NC} ${WHITE}%-8s${NC} - %s\n" "$protocol" "$port" "$process"
fi
done
else
echo -e "${RED}Не удалось получить информацию о портах${NC}"
fi
echo ""
# Процессы
echo -e "${BOLD}${BLUE}════════════════════════ PROCESSES INFO ══════════════════════${NC}"
if [ -n "$process_data" ]; then
process_count=$(echo "$process_data" | wc -l)
echo -e "${BOLD}Всего процессов:${NC} $process_count"
zombie_count=$(echo "$process_data" | awk '{if ($8 == "Z") print}' | wc -l)
if [ $zombie_count -gt 0 ]; then
echo -e "${BOLD}Zombie процессы:${NC} ${RED}$zombie_count${NC}"
else
echo -e "${BOLD}Zombie процессы:${NC} $zombie_count"
fi
# TOP 5 процессов по памяти
echo -e "${BOLD}${BLUE}═══════════════════ TOP 5 PROCESSES (MEMORY) ═════════════════${NC}"
echo "$process_data" | awk '{
cmd = $11
gsub(/^.*\//, "", cmd)
if (cmd == "") cmd = "[unknown]"
mem[cmd] += $6
} END {
for (cmd in mem) {
printf "%.2f|%s\n", mem[cmd]/1024, cmd
}
}' | sort -rn | head -5 | nl -w2 -s'. ' | while read line; do
number=$(echo $line | cut -d'.' -f1)
data=$(echo $line | cut -d'.' -f2-)
mem=$(echo $data | cut -d'|' -f1)
cmd=$(echo $data | cut -d'|' -f2-)
printf "%2d. ${WHITE}%.2f MB${NC} - %s\n" $number $mem "$cmd"
done
# TOP 5 процессов по CPU
echo -e "${BOLD}${BLUE}═════════════════════ TOP 5 PROCESSES (CPU) ══════════════════${NC}"
echo "$process_data" | awk '{
cmd = $11
gsub(/^.*\//, "", cmd)
if (cmd == "") cmd = "[unknown]"
cpu[cmd] += $3
} END {
for (cmd in cpu) {
printf "%.1f|%s\n", cpu[cmd], cmd
}
}' | sort -rn | head -5 | nl -w2 -s'. ' | while read line; do
number=$(echo $line | cut -d'.' -f1)
data=$(echo $line | cut -d'.' -f2-)
cpu=$(echo $data | cut -d'|' -f1)
cmd=$(echo $data | cut -d'|' -f2-)
printf "%2d. ${WHITE}%.1f%%${NC} - %s\n" $number $cpu "$cmd"
done
fi
echo ""
# Docker контейнеры (быстрая проверка)
if command -v docker &> /dev/null && docker ps &> /dev/null; then
echo -e "${BOLD}${BLUE}══════════════════════ DOCKER CONTAINERS ═════════════════════${NC}"
docker_not_running=()
while read line; do
name=$(echo $line | awk '{print $1}')
status=$(echo $line | awk '{print $2}')
if [[ $status == "Up"* ]]; then
echo -e "$name: ${GREEN}${NC} $status"
else
echo -e "$name: ${RED}${NC} $status"
docker_not_running+=("$name")
fi
done < <(docker ps --format "table {{.Names}}\t{{.Status}}" 2>/dev/null | tail -n+2)
echo ""
fi
# РЕЗЮМЕ
echo -e "${BOLD}${BLUE}════════════════════════════ RESUME ══════════════════════════${NC}"
echo ""
issues_found=0
# Проверка дисков (< 5% свободного места)
for mountpoint in "${!disk_usage[@]}"; do
used_percent=${disk_usage["$mountpoint"]}
free_percent=$((100 - used_percent))
if [ $free_percent -lt 5 ]; then
echo -e "${RED}⚠ ДИСК:${NC} На разделе ${CYAN}$mountpoint${NC} осталось менее 5% свободного места (${RED}${used_percent}% занято${NC})"
echo -e " ${YELLOW}Рекомендация:${NC} Очистите диск или увеличьте его размер"
issues_found=1
fi
done
# Проверка RAM (< 5% свободной)
free_ram_percent=$((100 - mem_percent))
if [ $free_ram_percent -lt 5 ]; then
echo -e "${RED}⚠ ПАМЯТЬ:${NC} Осталось менее 5% свободной RAM (${RED}${mem_percent}% занято${NC})"
echo -e " ${YELLOW}Рекомендация:${NC} Проверьте процессы, потребляющие память, и увеличьте RAM при необходимости"
issues_found=1
fi
# Проверка использования swap (> 5%)
if [ $swap_total -gt 0 ] && [ $swap_percent -gt 5 ]; then
echo -e "${YELLOW}⚠ SWAP:${NC} Используется более 5% swap (${YELLOW}${swap_percent}%${NC})"
echo -e " ${YELLOW}Рекомендация:${NC} Активное использование swap замедляет работу системы. Возможно, не хватает оперативной памяти."
issues_found=1
fi
# Проверка load average (> количество ядер)
if (( $(echo "$load_1min > $cpu_count" | bc -l 2>/dev/null) )); then
echo -e "${RED}⚠ CPU:${NC} Load average (${RED}$load_1min${NC}) превышает количество ядер CPU (${WHITE}$cpu_count${NC})"
echo -e " ${YELLOW}Рекомендация:${NC} Требуется увеличить количество CPU, возможно их не хватает"
issues_found=1
fi
# Проверка IOWait (> 10%)
if (( $(echo "$iowait_value > 10" | bc -l 2>/dev/null) )); then
echo -e "${RED}⚠ IOWAIT:${NC} Высокий IOWait (${RED}${iowait_value}%${NC})"
if [ ${#high_iowait_disks[@]} -gt 0 ]; then
echo -e " Проблемные диски: ${YELLOW}${high_iowait_disks[*]}${NC}"
fi
echo -e " ${YELLOW}Рекомендация:${NC} Проверьте дисковую подсистему, возможно медленные диски или проблемы с вводом-выводом"
issues_found=1
fi
# Проверка ошибок на интерфейсах
if [ ${#interface_errors[@]} -gt 0 ]; then
echo -e "${RED}⚠ СЕТЕВЫЕ ОШИБКИ:${NC} Обнаружены ошибки на сетевых интерфейсах:"
for interface in "${!interface_errors[@]}"; do
IFS=',' read -r rx_err tx_err rx_drop tx_drop <<< "${interface_errors[$interface]}"
echo -e " ${CYAN}$interface${NC}: RX errors: ${RED}$rx_err${NC}, TX errors: ${RED}$tx_err${NC}, RX drops: ${YELLOW}$rx_drop${NC}, TX drops: ${YELLOW}$tx_drop${NC}"
done
echo -e " ${YELLOW}Рекомендация:${NC} Проверьте сетевые подключения и кабели"
issues_found=1
fi
# Проверка файловых дескрипторов
if [ $file_handles_warning -eq 1 ]; then
echo -e "${YELLOW}⚠ ФАЙЛОВЫЕ ДЕСКРИПТОРЫ:${NC} Использовано более 80% доступных файловых дескрипторов (${file_handle_percent}%)"
echo -e " ${YELLOW}Рекомендация:${NC} Проверьте утечки файловых дескрипторов в приложениях"
issues_found=1
fi
# Проверка Docker контейнеров
if [ ${#docker_not_running[@]} -gt 0 ]; then
echo -e "${RED}⚠ DOCKER:${NC} Следующие контейнеры не запущены:"
for container in "${docker_not_running[@]}"; do
echo -e " - ${RED}$container${NC}"
done
issues_found=1
fi
if [ $issues_found -eq 0 ]; then
echo -e "${GREEN}✓ Система работает в штатном режиме. Проблем не обнаружено.${NC}"
fi
echo -e "${BOLD}${BLUE}══════════════════════════════════════════════════════════════${NC}"