Skip to content

Instantly share code, notes, and snippets.

@lanefu
Last active July 20, 2025 19:58
Show Gist options
  • Save lanefu/fcf57a0e04289809de428e1e9a7d5fcd to your computer and use it in GitHub Desktop.
Save lanefu/fcf57a0e04289809de428e1e9a7d5fcd to your computer and use it in GitHub Desktop.
smartclt drive hours report
#!/bin/bash
echo "Generating Drive Report (Drive Path, Serial Number, Power-On Hours)"
echo "-------------------------------------------------------------------"
# Get a list of all block devices that are likely physical disks
DRIVES=$(lsblk -o KNAME,TYPE -nr | awk '$2=="disk" {print "/dev/"$1}')
if [ -z "$DRIVES" ]; then
echo "No disk drives found."
exit 0
fi
# Header for the report
printf "%-18s %-25s %s\n" "Drive Path" "Serial Number" "Power-On Hours"
printf "%-18s %-25s %s\n" "----------" "-------------" "--------------"
for DRIVE_PATH in $DRIVES; do
SERIAL_NUMBER="N/A"
POWER_ON_HOURS="N/A" # Default to N/A
# --- Get Serial Number (using smartctl -i) ---
SERIAL_OUTPUT=""
if [[ "$DRIVE_PATH" == "/dev/nvme"* ]]; then
SERIAL_OUTPUT=$(sudo smartctl -i "$DRIVE_PATH" 2>/dev/null)
# Fallback to nvme-cli for serial if smartctl fails or is less reliable
if [ $? -ne 0 ] || ! echo "$SERIAL_OUTPUT" | grep -q "Serial Number"; then
if command -v nvme >/dev/null 2>&1; then
NVME_ID_CTRL=$(sudo nvme id-ctrl "$DRIVE_PATH" 2>/dev/null)
if [ $? -eq 0 ] && echo "$NVME_ID_CTRL" | grep -q "sn"; then
SERIAL_NUMBER=$(echo "$NVME_ID_CTRL" | grep "sn" | awk '{print $NF}')
fi
fi
fi
else
SERIAL_OUTPUT=$(sudo smartctl -i -d sat "$DRIVE_PATH" 2>/dev/null)
if [ $? -ne 0 ]; then
SERIAL_OUTPUT=$(sudo smartctl -i "$DRIVE_PATH" 2>/dev/null) # Try without -d sat
fi
fi
if [ -n "$SERIAL_OUTPUT" ] && [[ "$SERIAL_NUMBER" == "N/A" ]]; then
SERIAL_NUMBER=$(echo "$SERIAL_OUTPUT" | grep -i "Serial Number" | awk -F':' '{print $2}' | xargs)
fi
# --- Get Power-On Hours ---
if [[ "$DRIVE_PATH" == "/dev/nvme"* ]]; then
# 1. Try nvme-cli first for NVMe drives (most reliable for native NVMe info)
if command -v nvme >/dev/null 2>&1; then
NVME_SMART_LOG=$(sudo nvme smart-log "$DRIVE_PATH" 2>/dev/null)
if [ $? -eq 0 ] && [ -n "$NVME_SMART_LOG" ]; then
# Extract Power On Hours (looks for line, splits by ':', takes second field, removes commas, prints)
POH_NVME=$(echo "$NVME_SMART_LOG" | grep -E "power_on_hours" | awk -F': ' '{gsub(/,/, "", $2); print $2}' | awk '{print $1}' | xargs)
if [ -n "$POH_NVME" ]; then
POWER_ON_HOURS="$POH_NVME"
else
# Extract Controller Busy Time (looks for line, splits by ':', takes second field, removes commas, prints)
CBT_NVME=$(echo "$NVME_SMART_LOG" | grep -E "controller_busy_time" | awk -F': ' '{gsub(/,/, "", $2); print $2}' | awk '{print $1}' | xargs)
if [ -n "$CBT_NVME" ]; then
POWER_ON_HOURS=$((CBT_NVME / 60)) # Convert minutes to hours
fi
fi
fi
fi
# 2. Fallback to smartctl -A for NVMe if nvme-cli didn't provide hours or isn't installed
# The smartctl -A output for NVMe is similar in format to nvme smart-log in your case.
if [[ "$POWER_ON_HOURS" == "N/A" ]]; then
ATTRIBUTES_OUTPUT=$(sudo smartctl -A "$DRIVE_PATH" 2>/dev/null)
if [ -n "$ATTRIBUTES_OUTPUT" ]; then
# Extract "Power On Hours:" or "Controller Busy Time:"
POH_SMARTCTL_NVME=$(echo "$ATTRIBUTES_OUTPUT" | grep -E "Power On Hours:" | awk -F': ' '{gsub(/,/, "", $2); print $2}' | awk '{print $1}' | xargs)
if [ -n "$POH_SMARTCTL_NVME" ]; then
POWER_ON_HOURS="$POH_SMARTCTL_NVME"
else
CBT_SMARTCTL_NVME=$(echo "$ATTRIBUTES_OUTPUT" | grep -E "Controller Busy Time:" | awk -F': ' '{gsub(/,/, "", $2); print $2}' | awk '{print $1}' | xargs)
if [ -n "$CBT_SMARTCTL_NVME" ]; then
POWER_ON_HOURS=$((CBT_SMARTCTL_NVME / 60))
fi
fi
fi
fi
else # Standard SATA/SAS drives (like /dev/sda, /dev/sdb, etc.)
ATTRIBUTES_OUTPUT=$(sudo smartctl -A -d sat "$DRIVE_PATH" 2>/dev/null)
if [ $? -ne 0 ]; then
ATTRIBUTES_OUTPUT=$(sudo smartctl -A "$DRIVE_PATH" 2>/dev/null) # Try without -d sat
fi
if [ -n "$ATTRIBUTES_OUTPUT" ]; then
# For SATA, precisely extract the raw value of Power_On_Hours
# This regex captures digits that are either at the end of the line OR
# are followed by one or more spaces and an opening parenthesis.
RAW_HOURS_VALUE=$(echo "$ATTRIBUTES_OUTPUT" | \
grep -E "^\s*9\s+Power_On_Hours|Power_On_Hours" | \
grep -oP '\s\K\d+(?=\s+\(|\s*$)' | head -n 1) # Gets the number before ( or end of line
if [ -n "$RAW_HOURS_VALUE" ]; then
# Remove commas, if any (not in your examples, but good practice)
CLEAN_HOURS=$(echo "$RAW_HOURS_VALUE" | tr -d ',' | xargs)
if [ -n "$CLEAN_HOURS" ]; then
POWER_ON_HOURS="$CLEAN_HOURS"
fi
fi
fi
fi
# Print the results for the current drive
printf "%-18s %-25s %s\n" "$DRIVE_PATH" "${SERIAL_NUMBER:-N/A}" "${POWER_ON_HOURS:-N/A}"
done
echo "-------------------------------------------------------------------"
echo "Note: 'N/A' indicates data could not be retrieved (e.g., no SMART support, permission issues, or specific attribute not present)."
echo "For NVMe drives, ensure 'nvme-cli' is installed for best results (sudo apt install nvme-cli)."
#!/bin/bash
echo "Generating Drive Report for Synology (Drive Path, Serial Number, Power-On Hours)"
echo "-------------------------------------------------------------------"
# Initialize an array to store detected drive paths
declare -a DRIVES
# --- Attempt to find Synology-specific SATA/SAS drives (e.g., /dev/sata1, /dev/sata2) ---
# Iterate through common Synology SATA device names (e.g., sata1 up to sata24, covering most models)
# Adjust the upper limit (e.g., 24) if you have a DiskStation/RackStation with more bays.
for DEV_NUM in $(seq 1 24); do # Checks /dev/sata1 to /dev/sata24
DRIVE_PATH="/dev/sata${DEV_NUM}"
# Check if the device exists and smartctl can identify it as a disk
# smartctl -i will return 0 if it can successfully get info from the device
if sudo smartctl -i -d sat "${DRIVE_PATH}" >/dev/null 2>&1 || \
sudo smartctl -i "${DRIVE_PATH}" >/dev/null 2>&1; then
DRIVES+=("${DRIVE_PATH}")
fi
done
if [ ${#DRIVES[@]} -eq 0 ]; then
echo "No disk drives found using /dev/sataX patterns, or smartctl could not access them."
echo "Ensure smartmontools is installed and you have permissions."
exit 0
fi
# Header for the report
printf "%-18s %-25s %s\n" "Drive Path" "Serial Number" "Power-On Hours"
printf "%-18s %-25s %s\n" "----------" "-------------" "--------------"
for DRIVE_PATH in "${DRIVES[@]}"; do
SERIAL_NUMBER="N/A"
POWER_ON_HOURS="N/A"
# --- Get Serial Number (using smartctl -i) ---
SERIAL_OUTPUT=$(sudo smartctl -i -d sat "$DRIVE_PATH" 2>/dev/null)
if [ $? -ne 0 ]; then
SERIAL_OUTPUT=$(sudo smartctl -i "$DRIVE_PATH" 2>/dev/null) # Try without -d sat
fi
if [ -n "$SERIAL_OUTPUT" ]; then
SERIAL_NUMBER=$(echo "$SERIAL_OUTPUT" | grep -i "Serial Number" | awk -F':' '{print $2}' | xargs)
fi
# --- Get Power-On Hours (using smartctl -A) ---
ATTRIBUTES_OUTPUT=$(sudo smartctl -A -d sat "$DRIVE_PATH" 2>/dev/null)
if [ $? -ne 0 ]; then
ATTRIBUTES_OUTPUT=$(sudo smartctl -A "$DRIVE_PATH" 2>/dev/null) # Try without -d sat
fi
if [ -n "$ATTRIBUTES_OUTPUT" ]; then
# Common pattern: ID 9, Power_On_Hours
POWER_ON_HOURS=$(echo "$ATTRIBUTES_OUTPUT" | grep -E "^\s*9\s+Power_On_Hours" | awk '{print $NF}')
# Fallback for other common patterns if ID 9 is not Power_On_Hours
if [ -z "$POWER_ON_HOURS" ]; then
POWER_ON_HOURS=$(echo "$ATTRIBUTES_OUTPUT" | grep -E "Power_On_Hours" | awk '{print $NF}')
fi
# Add more specific alternative checks here if you discover them for your Synology drives
# For example, if you find "Drive_Uptime_Minutes" in your 'smartctl -a' output:
# if [ -z "$POWER_ON_HOURS" ]; then
# MINUTES_VAL=$(echo "$ATTRIBUTES_OUTPUT" | grep -E "Drive_Uptime_Minutes" | awk '{print $NF}')
# if [ -n "$MINUTES_VAL" ]; then
# POWER_ON_HOURS=$((MINUTES_VAL / 60))
# fi
# fi
fi
# Print the results for the current drive
printf "%-18s %-25s %s\n" "$DRIVE_PATH" "${SERIAL_NUMBER:-N/A}" "${POWER_ON_HOURS:-N/A}"
done
echo "-------------------------------------------------------------------"
echo "Note: 'N/A' indicates data could not be retrieved (e.g., no SMART support, permission issues, or specific attribute not present)."
echo "Synology DSM usually has 'smartmontools' pre-installed."
echo "If you still get 'N/A' for some drives, manually inspect 'sudo smartctl -a <drive_path>' for those drives"
echo "to identify alternative attribute names for 'Power On Hours'."
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment