Про очередную полезную утилиту или когда smart status слишком уж smart

Зайчатки разума

Записная книжка айтишника

Про очередную полезную утилиту или когда smart status слишком уж smart

2026-02-12 00:06:22 — Evgeniy Shumilov

  В последнее время сильно подорожала не только память, но и ssd, стоимость на которые поднялась в 2 раза, а для некоторых моделей и выше. Из-за этого было принято решение разжиться каким-нибудь БУ диском с авито за более скромный прайс. В процессе изучения объявлений о продаже я неоднократно видел скриншоты windows утилиты Crystal Disk Mark. И мне понравилось то, как там представлены данные - есть оценка здоровья диска в процентах и общая оценка, например, 92% GOOD. При использовании же smartctl приходится имплементировать результаты самостоятельно, ведь overall-health result недостаточен. Да и в среднем количество вывода избыточно. Решил навайбкодить (и, естественно, доработать потом руками) обёртку поверх smartctl, которая бы позволяла делать быструю оценку состояния дисков. Так чтобы можно было запуском одной команды сразу вывести все статусы и основные характеристики всех дисков, их размер, температуру и т.п.. И подсветить проблемы, чтобы если что-то не так - это сразу бросилось в глаза. Под катом будет краткая справка и содержимое самого скрипта.


Справка

smartshot -h
smartshot v0.19 - SMART disk health checker

Usage:
  smartshot [options]
  smartshot <device> [options]
  smartshot <device1> <device2> ... [options]

Options:
  -i, --info      Show device information
  -s, --state     Show health state only
  -p, --percent   Show health percentage only
  -w, --worst     Show the most critical metric
  -t, --table     Show summary table for multiple devices
  -h, --help      Show this help message

Examples:
  smartshot                # Show table for all drives
  smartshot -i             # Show info for all drives
  smartshot -s             # Show state for all drives
  smartshot -p             # Show health percentage for all drives
  smartshot -w             # Show worst metric for all drives
  smartshot nvme0          # Basic info + state + percent for nvme0
  smartshot sda            # Basic info for SATA drive
  smartshot nvme0 -w       # Show worst metric for nvme0
  smartshot sda sdb -t     # Table for specific drives
  smartshot nvme0 -isp     # Combined flags (info + state + percent)

 

Сам код

Ссылка на скачивание

#!/bin/bash

# smartshot - SMART disk health checker by Evgeniy Shumilov <eashumilov@ya.ru>
# Version: 0.19 - FINAL WITH CORRECT AUTO-DETECT

set -euo pipefail

# Colors for output
if [ -t 1 ]; then
    RED='\033[0;31m'
    GREEN='\033[0;32m'
    YELLOW='\033[1;33m'
    BLUE='\033[0;34m'
    PURPLE='\033[0;35m'
    LIGHT_PURPLE='\033[1;95m'
    CYAN='\033[0;36m'
    LIGHT_CYAN='\033[1;96m'
    BOLD='\033[1m'
    NC='\033[0m'
else
    RED=''
    GREEN=''
    YELLOW=''
    BLUE=''
    PURPLE=''
    LIGHT_PURPLE=''
    CYAN=''
    LIGHT_CYAN=''
    BOLD=''
    NC=''
fi

VERSION="0.19"

check_smartctl() {
    if ! command -v smartctl &> /dev/null; then
        echo -e "${RED}Error: smartctl not found${NC}" >&2
        exit 1
    fi
}

# Get all disks via smartctl --scan
get_all_disks() {
    local disks=()
    while IFS= read -r line; do
        if [[ "$line" =~ ^(/dev/[^[:space:]]+) ]]; then
            disks+=("$(basename "${BASH_REMATCH[1]}")")
        fi
    done < <(smartctl --scan 2>/dev/null)
    printf '%s\n' "${disks[@]}" | sort -u
}

# Universal number cleaner - removes ALL non-digit characters
clean_number() {
    echo "$1" | sed -E 's/[^0-9]//g'
}

# Temperature color
get_temp_color() {
    local temp=$1
    if [ -z "$temp" ] || [ "$temp" -lt 0 ]; then
        echo "${NC}"
    elif [ "$temp" -ge 70 ]; then
        echo "${RED}"
    elif [ "$temp" -ge 60 ]; then
        echo "${YELLOW}"
    elif [ "$temp" -ge 50 ]; then
        echo "${YELLOW}"
    else
        echo "${GREEN}"
    fi
}

# NVMe function - UNIVERSAL
get_nvme_data() {
    local device="$1"
    local -n _data=$2
    
    _data[MODEL]=""
    _data[SERIAL]=""
    _data[FIRMWARE]=""
    _data[CAPACITY]=""
    _data[CAPACITY_RAW]=""
    _data[TYPE]="NVMe SSD"
    _data[SMART_STATUS]=""
    _data[TEMP]=""
    _data[PERCENTAGE_USED]=""
    _data[POWER_ON_HOURS]=""
    _data[POWER_CYCLES]=""
    _data[AVAIL_SPARE]=""
    _data[HEALTH_SCORE]="100"
    
    local dev="/dev/$(basename "$device")"
    local smart_info=$(smartctl -i "$dev" 2>/dev/null)
    local smart_health=$(smartctl -H "$dev" 2>/dev/null)
    local smart_attr=$(smartctl -A "$dev" 2>/dev/null)
    
    _data[MODEL]=$(echo "$smart_info" | grep -i "Model Number:" | head -1 | cut -d: -f2- | sed -e 's/^[ \t]*//' -e 's/[ \t]*$//')
    _data[SERIAL]=$(echo "$smart_info" | grep -i "Serial Number:" | head -1 | cut -d: -f2- | sed -e 's/^[ \t]*//' -e 's/[ \t]*$//')
    _data[FIRMWARE]=$(echo "$smart_info" | grep -i "Firmware Version:" | head -1 | cut -d: -f2- | sed -e 's/^[ \t]*//' -e 's/[ \t]*$//')
    
    local cap_line=$(echo "$smart_info" | grep -i "Namespace 1 Size/Capacity:" | head -1)
    if [[ "$cap_line" =~ \[([0-9,.]+)[[:space:]]*([A-Z]+)\] ]]; then
        _data[CAPACITY]=$(echo "${BASH_REMATCH[1]}" | sed 's/[^0-9]//g')
        _data[CAPACITY_RAW]="$(echo "${BASH_REMATCH[1]}" | sed 's/[^0-9.]//g')${BASH_REMATCH[2]}"
    fi
    
    _data[SMART_STATUS]=$(echo "$smart_health" | grep -i "SMART overall-health" | cut -d: -f2- | sed -e 's/^[ \t]*//' -e 's/[ \t]*$//')
    
    local temp_line=$(echo "$smart_attr" | grep -i "^Temperature:" | head -1)
    [[ "$temp_line" =~ ([0-9]+) ]] && _data[TEMP]="${BASH_REMATCH[1]}"
    
    local used_line=$(echo "$smart_attr" | grep -i "Percentage Used:" | head -1)
    [[ "$used_line" =~ ([0-9]+)% ]] && _data[PERCENTAGE_USED]="${BASH_REMATCH[1]}"
    
    # UNIVERSAL hours extraction - take everything after colon and remove NON-digits
    local hours_line=$(echo "$smart_attr" | grep -i "Power On Hours:" | head -1)
    if [ -n "$hours_line" ]; then
        local hours_raw=$(echo "$hours_line" | cut -d: -f2- | sed -e 's/^[ \t]*//' -e 's/[ \t]*$//')
        _data[POWER_ON_HOURS]=$(clean_number "$hours_raw")
    fi
    
    # UNIVERSAL power cycles extraction
    local cycles_line=$(echo "$smart_attr" | grep -i "Power Cycles:" | head -1)
    if [ -n "$cycles_line" ]; then
        local cycles_raw=$(echo "$cycles_line" | cut -d: -f2- | sed -e 's/^[ \t]*//' -e 's/[ \t]*$//')
        _data[POWER_CYCLES]=$(clean_number "$cycles_raw")
    fi
    
    local spare_line=$(echo "$smart_attr" | grep -i "Available Spare:" | head -1)
    [[ "$spare_line" =~ ([0-9]+)% ]] && _data[AVAIL_SPARE]="${BASH_REMATCH[1]}"
    
    if [ -n "${_data[PERCENTAGE_USED]}" ]; then
        _data[HEALTH_SCORE]=$((100 - ${_data[PERCENTAGE_USED]}))
    fi
    
    [ -n "${_data[MODEL]}" ]
}

# ATA/SATA function - UNIVERSAL
get_ata_data() {
    local device="$1"
    local -n _data=$2
    
    _data[MODEL]=""
    _data[SERIAL]=""
    _data[FIRMWARE]=""
    _data[CAPACITY]=""
    _data[CAPACITY_RAW]=""
    _data[TYPE]=""
    _data[SMART_STATUS]=""
    _data[TEMP]=""
    _data[POWER_ON_HOURS]=""
    _data[POWER_CYCLES]=""
    _data[REALLOCATED_SECTORS]=""
    _data[PENDING_SECTORS]=""
    _data[CRC_ERRORS]=""
    _data[HEALTH_SCORE]="100"
    
    local dev="/dev/$(basename "$device")"
    local smart_info=$(smartctl -i "$dev" 2>/dev/null)
    local smart_health=$(smartctl -H "$dev" 2>/dev/null)
    local smart_attr=$(smartctl -A "$dev" 2>/dev/null)
    
    _data[MODEL]=$(echo "$smart_info" | grep -i "Device Model:" | head -1 | cut -d: -f2- | sed -e 's/^[ \t]*//' -e 's/[ \t]*$//')
    _data[SERIAL]=$(echo "$smart_info" | grep -i "Serial Number:" | head -1 | cut -d: -f2- | sed -e 's/^[ \t]*//' -e 's/[ \t]*$//')
    _data[FIRMWARE]=$(echo "$smart_info" | grep -i "Firmware Version:" | head -1 | cut -d: -f2- | sed -e 's/^[ \t]*//' -e 's/[ \t]*$//')
    
    local cap_line=$(echo "$smart_info" | grep -i "User Capacity:" | head -1)
    if [[ "$cap_line" =~ \[([0-9,.]+)[[:space:]]*([A-Z]+)\] ]]; then
        local unit="${BASH_REMATCH[2]}"
        local value=$(echo "${BASH_REMATCH[1]}" | sed 's/[^0-9.]//g')
        if [ "$unit" = "TB" ]; then
            _data[CAPACITY_RAW]="${value}TB"
        elif [ "$unit" = "GB" ]; then
            _data[CAPACITY_RAW]="${value}GB"
        fi
    fi
    
    local rotation=$(echo "$smart_info" | grep -i "Rotation Rate:" | head -1)
    if echo "$rotation" | grep -qi "solid state" || echo "$rotation" | grep -qi "0 rpm"; then
        _data[TYPE]="SATA SSD"
    else
        _data[TYPE]="HDD"
    fi
    
    _data[SMART_STATUS]=$(echo "$smart_health" | grep -i "SMART overall-health" | cut -d: -f2- | sed -e 's/^[ \t]*//' -e 's/[ \t]*$//')
    
    while IFS= read -r line; do
        if [[ "$line" =~ ^[[:space:]]*([0-9]+)[[:space:]]+([^[:space:]]+) ]]; then
            local id="${BASH_REMATCH[1]}"
            local values=($line)
            
            case "$id" in
                9)  _data[POWER_ON_HOURS]="${values[9]:-0}" ;;
                12) _data[POWER_CYCLES]="${values[9]:-0}" ;;
                194) _data[TEMP]="${values[9]:-0}" ;;
                5)  _data[REALLOCATED_SECTORS]="${values[9]:-0}" ;;
                197) _data[PENDING_SECTORS]="${values[9]:-0}" ;;
                199) _data[CRC_ERRORS]="${values[9]:-0}" ;;
                169) _data[HEALTH_SCORE]="${values[3]:-100}" ;;
                232) _data[HEALTH_SCORE]="${values[3]:-100}" ;;
            esac
        fi
    done <<< "$smart_attr"
    
    [ -n "${_data[MODEL]}" ]
}

is_nvme() {
    [[ "$(basename "$1")" =~ ^nvme[0-9]+ ]]
}

get_health_rating() {
    local score=$1
    if [ "$score" -ge 90 ]; then echo "Excellent"
    elif [ "$score" -ge 70 ]; then echo "Good"
    elif [ "$score" -ge 50 ]; then echo "Fair"
    elif [ "$score" -ge 25 ]; then echo "Poor"
    else echo "Critical"
    fi
}

get_health_color() {
    local score=$1
    if [ "$score" -ge 90 ]; then echo "${GREEN}"
    elif [ "$score" -ge 70 ]; then echo "${GREEN}"
    elif [ "$score" -ge 50 ]; then echo "${YELLOW}"
    elif [ "$score" -ge 25 ]; then echo "${RED}"
    else echo "${RED}"
    fi
}

# Output functions
print_info() {
    local device=$1
    local -n _data=$2
    
    echo -e "${BOLD}Device:${NC} ${LIGHT_CYAN}$device${NC}"
    echo -e "${BOLD}Model:${NC} ${LIGHT_CYAN}${_data[MODEL]:-N/A}${NC}"
    echo -e "${BOLD}Serial:${NC} ${LIGHT_CYAN}${_data[SERIAL]:-N/A}${NC}"
    echo -e "${BOLD}Firmware:${NC} ${LIGHT_CYAN}${_data[FIRMWARE]:-N/A}${NC}"
    
    if [ -n "${_data[CAPACITY_RAW]}" ]; then
        echo -e "${BOLD}Capacity:${NC} ${LIGHT_CYAN}${_data[CAPACITY_RAW]}${NC}"
    else
        echo -e "${BOLD}Capacity:${NC} ${LIGHT_CYAN}${_data[CAPACITY]:-N/A} GB${NC}"
    fi
    
    echo -e "${BOLD}Type:${NC} ${LIGHT_CYAN}${_data[TYPE]:-N/A}${NC}"
    echo -e "${BOLD}SMART:${NC} ${_data[SMART_STATUS]:-N/A}"
    
    local temp_color=$(get_temp_color "${_data[TEMP]}")
    echo -e "${BOLD}Temperature:${NC} ${temp_color}${_data[TEMP]:-N/A}°C${NC}"
    echo -e "${BOLD}Power On Hours:${NC} ${_data[POWER_ON_HOURS]:-0} h"
    echo -e "${BOLD}Power Cycles:${NC} ${_data[POWER_CYCLES]:-0}"
    
    if [ "${_data[TYPE]}" = "NVMe SSD" ]; then
        echo -e "${BOLD}Percentage Used:${NC} ${_data[PERCENTAGE_USED]:-0}%"
        echo -e "${BOLD}Available Spare:${NC} ${_data[AVAIL_SPARE]:-100}%"
    else
        local realloc_color="${GREEN}"
        [ "${_data[REALLOCATED_SECTORS]}" -gt 0 ] && realloc_color="${YELLOW}"
        [ "${_data[REALLOCATED_SECTORS]}" -gt 100 ] && realloc_color="${RED}"
        echo -e "${BOLD}Reallocated Sectors:${NC} ${realloc_color}${_data[REALLOCATED_SECTORS]:-0}${NC}"
        
        local pending_color="${GREEN}"
        [ "${_data[PENDING_SECTORS]}" -gt 0 ] && pending_color="${YELLOW}"
        [ "${_data[PENDING_SECTORS]}" -gt 10 ] && pending_color="${RED}"
        echo -e "${BOLD}Pending Sectors:${NC} ${pending_color}${_data[PENDING_SECTORS]:-0}${NC}"
        
        local crc_color="${GREEN}"
        [ "${_data[CRC_ERRORS]}" -gt 0 ] && crc_color="${YELLOW}"
        [ "${_data[CRC_ERRORS]}" -gt 100 ] && crc_color="${RED}"
        echo -e "${BOLD}CRC Errors:${NC} ${crc_color}${_data[CRC_ERRORS]:-0}${NC}"
    fi
    
    local health_score=${_data[HEALTH_SCORE]:-100}
    local health_color=$(get_health_color $health_score)
    local health_rating=$(get_health_rating $health_score)
    echo ""
    echo -e "${LIGHT_PURPLE}STATE:${NC} ${health_color}${health_rating}${NC}"
    echo -e "${LIGHT_PURPLE}HEALTH:${NC} ${health_color}${health_score}%${NC}"
}

print_state() {
    local -n _data=$1
    local score=${_data[HEALTH_SCORE]:-100}
    local color=$(get_health_color $score)
    echo -e "${color}$(get_health_rating $score)${NC}"
}

print_worst() {
    local device=$1
    local -n _data=$2
    
    local worst=""
    local value=""
    local comment=""
    local color="${YELLOW}"
    
    if [ "${_data[TYPE]}" = "NVMe SSD" ]; then
        if [ -n "${_data[PERCENTAGE_USED]}" ] && [ "${_data[PERCENTAGE_USED]}" -ge 70 ]; then
            worst="Percentage Used"
            value="${_data[PERCENTAGE_USED]}%"
            comment="Drive is aging"
            [ "${_data[PERCENTAGE_USED]}" -ge 90 ] && color="${RED}"
        fi
    else
        if [ -n "${_data[REALLOCATED_SECTORS]}" ] && [ "${_data[REALLOCATED_SECTORS]}" -gt 0 ]; then
            worst="Reallocated Sectors"
            value="${_data[REALLOCATED_SECTORS]}"
            if [ "${_data[REALLOCATED_SECTORS]}" -gt 100 ]; then
                comment="CRITICAL: Replace drive"
                color="${RED}"
            else
                comment="Drive degrading"
                color="${YELLOW}"
            fi
        fi
    fi
    
    if [ -n "${_data[TEMP]}" ] && [ "${_data[TEMP]}" -ge 70 ]; then
        worst="Temperature"
        value="${_data[TEMP]}°C"
        comment="Critical temperature"
        color="${RED}"
    elif [ -n "${_data[TEMP]}" ] && [ "${_data[TEMP]}" -ge 60 ]; then
        worst="Temperature"
        value="${_data[TEMP]}°C"
        comment="High temperature"
        color="${YELLOW}"
    fi
    
    if [ -n "$worst" ]; then
        echo -e "${BOLD}${LIGHT_CYAN}${device}${NC}: $worst = ${value}"
        echo -e "  → ${color}${comment}${NC}"
    else
        echo -e "${GREEN}${LIGHT_CYAN}${device}${NC}: No critical issues detected${NC}"
    fi
}

draw_scale() {
    local value=$1
    local width=$2
    local filled=$((width * value / 100))
    local empty=$((width - filled))
    
    if [ "$value" -ge 90 ]; then echo -ne "${GREEN}"
    elif [ "$value" -ge 70 ]; then echo -ne "${GREEN}"
    elif [ "$value" -ge 50 ]; then echo -ne "${YELLOW}"
    else echo -ne "${RED}"
    fi
    
    printf '%*s' $filled | tr ' ' '█'
    echo -ne "${NC}"
    printf '%*s' $empty | tr ' ' '░'
}

print_metrics() {
    local -n _data=$1
    
    echo -e "${BOLD}Health:${NC}"
    echo -n "  "
    draw_scale ${_data[HEALTH_SCORE]:-100} 40
    echo -e " ${_data[HEALTH_SCORE]:-100}%"
    
    if [ "${_data[TYPE]}" = "NVMe SSD" ] && [ -n "${_data[PERCENTAGE_USED]}" ]; then
        echo -e "\n${BOLD}Usage:${NC}"
        echo -n "  "
        draw_scale $((100 - ${_data[PERCENTAGE_USED]})) 40
        echo -e " ${_data[PERCENTAGE_USED]}% used"
    fi
    
    if [ -n "${_data[TEMP]}" ]; then
        echo -e "\n${BOLD}Temperature:${NC}"
        local temp_score=100
        [ "${_data[TEMP]}" -gt 60 ] && temp_score=$((100 - (${_data[TEMP]} - 60) * 5))
        [ $temp_score -lt 0 ] && temp_score=0
        echo -n "  "
        draw_scale $temp_score 40
        echo -e " ${_data[TEMP]}°C"
    fi
}

print_table() {
    local -n _devices=$1
    local -n _data=$2
    
    # Top border
    echo -e "${BOLD}+-----------------------------------------------------------------------------------------------------------------------------------+${NC}"
    
    # Header
    echo -e "${BOLD}| DISK HEALTH SUMMARY                                                                                                               |${NC}"
    
    # Separator
    echo -e "${BOLD}|-----------------------------------------------------------------------------------------------------------------------------------|${NC}"
    
    # Column headers
    echo -e "${BOLD}| DEVICE     | MODEL                     | SERIAL               | CAPACITY | TYPE     | HEALTH | STATUS         | TEMP   | HOURS    |${NC}"
    
    # Separator
    echo -e "${BOLD}|-----------------------------------------------------------------------------------------------------------------------------------|${NC}"
    
    # Data
    for device in "${_devices[@]}"; do
        local dev_name=$(basename "$device")
        local health="${_data[${device}_HEALTH_SCORE]:-100}"
        local health_color=$(get_health_color $health)
        local rating=$(get_health_rating $health)
        local model="${_data[${device}_MODEL]:-N/A}"
        local serial="${_data[${device}_SERIAL]:-N/A}"
        local capacity="${_data[${device}_CAPACITY_RAW]:-N/A}"
        local type="${_data[${device}_TYPE]:-N/A}"
        local temp="${_data[${device}_TEMP]:-N/A}"
        local temp_color=$(get_temp_color "$temp")
        local hours="${_data[${device}_POWER_ON_HOURS]:-0}"
        
        # Trim model if too long
        if [ ${#model} -gt 25 ]; then
            model="${model:0:22}..."
        fi
        
        # Trim serial if too long
        if [ ${#serial} -gt 20 ]; then
            serial="${serial:0:17}..."
        fi
        
        # Output data row
        echo -ne "${BOLD}|${NC} "
        echo -ne "${LIGHT_CYAN}"
        printf "%-10s" "$dev_name"
        echo -ne "${NC} | "
        echo -ne "${LIGHT_CYAN}"
        printf "%-25s" "$model"
        echo -ne "${NC} | "
        echo -ne "${LIGHT_CYAN}"
        printf "%-20s" "$serial"
        echo -ne "${NC} | "
        printf "%-8s" "$capacity"
        echo -n " | "
        printf "%-8s" "$type"
        echo -n " | "
        echo -ne "${health_color}"
        printf "%-6s" "${health}%"
        echo -ne "${NC} | "
        echo -ne "${health_color}"
        printf "%-14s" "$rating"
        echo -ne "${NC} | "
        echo -ne "${temp_color}"
        printf "%-7s" "${temp}°C"
        echo -ne "${NC} | "
        printf "%-8s" "$hours"
        echo -e " ${BOLD}|${NC}"
    done
    
    # Bottom border
    echo -e "${BOLD}+-----------------------------------------------------------------------------------------------------------------------------------+${NC}"
}

show_help() {
    echo -e "${BOLD}smartshot v${VERSION} - SMART disk health checker${NC}"
    echo ""
    echo -e "${BOLD}Usage:${NC}"
    echo "  smartshot [options]"
    echo "  smartshot <device> [options]"
    echo "  smartshot <device1> <device2> ... [options]"
    echo ""
    echo -e "${BOLD}Options:${NC}"
    echo "  -i, --info      Show device information"
    echo "  -s, --state     Show health state only"
    echo "  -p, --percent   Show health percentage only"
    echo "  -w, --worst     Show the most critical metric"
    echo "  -t, --table     Show summary table for multiple devices"
    echo "  -h, --help      Show this help message"
    echo ""
    echo -e "${BOLD}Examples:${NC}"
    echo "  smartshot                # Show table for all drives"
    echo "  smartshot -i             # Show info for all drives"
    echo "  smartshot -s             # Show state for all drives"
    echo "  smartshot -p             # Show health percentage for all drives"
    echo "  smartshot -w             # Show worst metric for all drives"
    echo "  smartshot nvme0          # Basic info + state + percent for nvme0"
    echo "  smartshot sda            # Basic info for SATA drive"
    echo "  smartshot nvme0 -w       # Show worst metric for nvme0"
    echo "  smartshot sda sdb -t     # Table for specific drives"
    echo "  smartshot nvme0 -isp     # Combined flags (info + state + percent)"
}

# MAIN
check_smartctl

# Parse arguments
DEVICES=()
OPT_INFO=false
OPT_STATE=false
OPT_PERCENT=false
OPT_WORST=false
OPT_TABLE=false
OPT_HELP=false

# If no arguments - show table for all drives
if [ $# -eq 0 ]; then
    OPT_TABLE=true
fi

while [ $# -gt 0 ]; do
    case "$1" in
        -i|--info) OPT_INFO=true; shift ;;
        -s|--state) OPT_STATE=true; shift ;;
        -p|--percent) OPT_PERCENT=true; shift ;;
        -w|--worst) OPT_WORST=true; shift ;;
        -t|--table) OPT_TABLE=true; shift ;;
        -h|--help) OPT_HELP=true; shift ;;
        -*)
            if [[ "$1" =~ ^-[iswthp]+$ ]]; then
                flags="${1#-}"
                for ((i=0; i<${#flags}; i++)); do
                    case "${flags:$i:1}" in
                        i) OPT_INFO=true ;;
                        s) OPT_STATE=true ;;
                        p) OPT_PERCENT=true ;;
                        w) OPT_WORST=true ;;
                        t) OPT_TABLE=true ;;
                        h) OPT_HELP=true ;;
                    esac
                done
                shift
            else
                echo -e "${RED}Unknown option: $1${NC}" >&2
                exit 1
            fi
            ;;
        *)
            DEVICES+=("$1")
            shift
            ;;
    esac
done

[ "$OPT_HELP" = true ] && { show_help; exit 0; }

# If no options and not table - show basic information
if ! $OPT_INFO && ! $OPT_STATE && ! $OPT_PERCENT && ! $OPT_WORST && ! $OPT_TABLE; then
    OPT_INFO=true
    OPT_STATE=true
    OPT_PERCENT=true
fi

# If no devices specified - get all disks via smartctl --scan
if [ ${#DEVICES[@]} -eq 0 ]; then
    mapfile -t DEVICES < <(get_all_disks)
fi

declare -A DISK_DATA
VALID_DEVICES=()

# Quiet mode for -s and -p (only when specific devices are specified and no other options)
QUIET_MODE=false
if [ "$OPT_STATE" = true ] && [ "$OPT_INFO" = false ] && [ "$OPT_WORST" = false ] && [ "$OPT_TABLE" = false ] && [ ${#DEVICES[@]} -eq 1 ]; then
    QUIET_MODE=true
fi
if [ "$OPT_PERCENT" = true ] && [ "$OPT_INFO" = false ] && [ "$OPT_WORST" = false ] && [ "$OPT_TABLE" = false ] && [ ${#DEVICES[@]} -eq 1 ]; then
    QUIET_MODE=true
fi

for dev in "${DEVICES[@]}"; do
    dev_name=$(basename "$dev")
    dev_path="/dev/$dev_name"
    
    # Show "Reading..." only if not quiet mode and not table
    if [ "$QUIET_MODE" = false ] && [ "$OPT_TABLE" = false ]; then
        echo -e "${BLUE}Reading $dev_path...${NC}" >&2
    fi
    
    if [ ! -e "$dev_path" ]; then
        [ "$QUIET_MODE" = false ] && [ "$OPT_TABLE" = false ] && echo -e "${RED}Error: Device $dev_path does not exist${NC}" >&2
        continue
    fi
    
    declare -A device_data
    
    if is_nvme "$dev_path"; then
        if get_nvme_data "$dev_path" device_data; then
            VALID_DEVICES+=("$dev_path")
            for key in "${!device_data[@]}"; do
                DISK_DATA["${dev_path}_${key}"]="${device_data[$key]}"
            done
            [ "$QUIET_MODE" = false ] && [ "$OPT_TABLE" = false ] && echo -e "${GREEN}✓ ${device_data[TYPE]} detected${NC}" >&2
        else
            [ "$QUIET_MODE" = false ] && [ "$OPT_TABLE" = false ] && echo -e "${RED}✗ Failed to read NVMe SMART data${NC}" >&2
        fi
    else
        if get_ata_data "$dev_path" device_data; then
            VALID_DEVICES+=("$dev_path")
            for key in "${!device_data[@]}"; do
                DISK_DATA["${dev_path}_${key}"]="${device_data[$key]}"
            done
            [ "$QUIET_MODE" = false ] && [ "$OPT_TABLE" = false ] && echo -e "${GREEN}✓ ${device_data[TYPE]} detected${NC}" >&2
        else
            [ "$QUIET_MODE" = false ] && [ "$OPT_TABLE" = false ] && echo -e "${RED}✗ Failed to read ATA SMART data${NC}" >&2
        fi
    fi
    [ "$QUIET_MODE" = false ] && [ "$OPT_TABLE" = false ] && echo >&2
done

[ ${#VALID_DEVICES[@]} -eq 0 ] && { 
    if [ "$OPT_TABLE" = true ]; then
        echo -e "${RED}No valid disks found${NC}" >&2
    else
        echo -e "${RED}Error: No valid devices found${NC}" >&2
    fi
    exit 1 
}

# Table
if [ "$OPT_TABLE" = true ]; then
    print_table VALID_DEVICES DISK_DATA
    exit 0
fi

# Output for each device with separator
FIRST_DEVICE=true
for device in "${VALID_DEVICES[@]}"; do
    declare -A current_data
    for key in MODEL SERIAL FIRMWARE CAPACITY CAPACITY_RAW TYPE SMART_STATUS TEMP \
               PERCENTAGE_USED POWER_ON_HOURS POWER_CYCLES HEALTH_SCORE \
               AVAIL_SPARE REALLOCATED_SECTORS PENDING_SECTORS CRC_ERRORS; do
        current_data[$key]="${DISK_DATA[${device}_${key}]:-}"
    done
    
    dev_name=$(basename "$device")
    
    # Separator between devices
    if [ "$FIRST_DEVICE" = false ]; then
        if [ "$OPT_INFO" = true ] || [ "$OPT_WORST" = true ]; then
            echo -e "${BOLD}────────────────────────────────────────────────${NC}"
            echo ""
        fi
    fi
    
    if [ "$OPT_INFO" = true ]; then
        print_info "$dev_name" current_data
    fi
    
    if [ "$OPT_STATE" = true ] && [ "$OPT_INFO" = false ]; then
        print_state current_data
    fi
    
    if [ "$OPT_PERCENT" = true ] && [ "$OPT_INFO" = false ]; then
        echo "${current_data[HEALTH_SCORE]:-100}"
    fi
    
    if [ "$OPT_WORST" = true ]; then
        [ "$OPT_INFO" = true ] && echo ""
        print_worst "$dev_name" current_data
    fi
    
    FIRST_DEVICE=false
done

Теги: админское, shell, instruments

comments powered by Disqus