Files
linuxScripts/systemState.sh
2026-02-18 09:54:44 +03:00

480 lines
20 KiB
Bash
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#!/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}"