commit 73483ff7dba83a6d8bcd6cac6855aaf8d681973c Author: pavel Date: Wed Feb 18 09:54:44 2026 +0300 Add systemState.sh diff --git a/systemState.sh b/systemState.sh new file mode 100644 index 0000000..9fa8c42 --- /dev/null +++ b/systemState.sh @@ -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}"