test-microsd.sh
Script to test a microSD card by mounting /dev/sdb partitions and logging results, run with sudo.
#!/usr/bin/env bash
set -uo pipefail
DEV="/dev/sdb"
BOOT="${DEV}1"
ROOT="${DEV}2"
LOG="$HOME/microsd-test-$(date +%Y%m%d-%H%M%S).log"
if [[ $EUID -ne 0 ]]; then
echo "Run this script with sudo:"
echo " sudo $0"
exit 1
fi
# If invoked with sudo, save the logfile in the real user's home.
if [[ -n "${SUDO_USER:-}" && "${SUDO_USER}" != "root" ]]; then
USER_HOME="$(getent passwd "$SUDO_USER" | cut -d: -f6)"
LOG="$USER_HOME/microsd-test-$(date +%Y%m%d-%H%M%S).log"
fi
exec > >(tee -a "$LOG") 2>&1
echo "======================================================"
echo " Raspberry Pi microSD diagnostic"
echo "======================================================"
echo
echo "Device: $DEV"
echo "Log: $LOG"
echo
# -------------------------------------------------------
# SAFETY CHECKS
# -------------------------------------------------------
if [[ ! -b "$DEV" ]]; then
echo "ERROR: $DEV does not exist."
exit 1
fi
ROOTDEV="$(findmnt -n -o SOURCE / | sed -E 's/p?[0-9]+$//')"
if [[ "$ROOTDEV" == "$DEV" ]]; then
echo "REFUSING TO CONTINUE."
echo "$DEV appears to contain the currently running OS."
exit 1
fi
echo "Detected device:"
lsblk -o NAME,PATH,SIZE,RM,RO,TYPE,FSTYPE,LABEL,MODEL,MOUNTPOINTS "$DEV"
echo
echo "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!"
echo " Verify that THIS is your 238 GB microSD card:"
echo
lsblk -d -o NAME,SIZE,RM,MODEL,SERIAL "$DEV"
echo
echo " This script performs READ-ONLY diagnostics."
echo " It WILL NOT format or overwrite the card."
echo "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!"
echo
read -rp "Type YES to test $DEV: " CONFIRM
if [[ "$CONFIRM" != "YES" ]]; then
echo "Cancelled."
exit 1
fi
echo
echo "======================================================"
echo " 1. Unmounting SD partitions"
echo "======================================================"
while read -r PART MOUNTPOINT; do
[[ -z "$MOUNTPOINT" ]] && continue
echo "Unmounting $PART from $MOUNTPOINT..."
umount "$PART" || {
echo "ERROR: Could not unmount $PART"
exit 1
}
done < <(lsblk -nrpo NAME,MOUNTPOINT "$DEV")
sync
echo
lsblk -o NAME,SIZE,FSTYPE,LABEL,MOUNTPOINTS "$DEV"
echo
echo "======================================================"
echo " 2. Checking kernel/device information"
echo "======================================================"
udevadm info --query=property --name="$DEV" 2>/dev/null |
grep -E 'ID_MODEL=|ID_VENDOR=|ID_SERIAL=|ID_BUS=|ID_DRIVE_FLASH|ID_DRIVE_REMOVABLE' ||
true
echo
echo "Partition table:"
fdisk -l "$DEV"
echo
echo "======================================================"
echo " 3. FAT boot partition filesystem check"
echo "======================================================"
if [[ -b "$BOOT" ]]; then
echo "Running READ-ONLY FAT check on $BOOT..."
echo
fsck.vfat -n -v "$BOOT"
FAT_RESULT=$?
echo
echo "fsck.vfat exit code: $FAT_RESULT"
else
echo "WARNING: $BOOT does not exist."
FAT_RESULT=99
fi
echo
echo "======================================================"
echo " 4. ext4 root partition filesystem check"
echo "======================================================"
if [[ -b "$ROOT" ]]; then
echo "Running READ-ONLY forced ext4 check on $ROOT..."
echo
e2fsck -f -n -v "$ROOT"
EXT_RESULT=$?
echo
echo "e2fsck exit code: $EXT_RESULT"
else
echo "WARNING: $ROOT does not exist."
EXT_RESULT=99
fi
echo
echo "======================================================"
echo " 5. Full physical READ scan of microSD"
echo "======================================================"
echo
echo "This reads the ENTIRE ~238 GiB card."
echo "Nothing will be written."
echo
echo "This can take a while depending on the USB adapter."
echo
START_TIME=$(date +%s)
# -s = progress
# -v = verbose summary
# -b 4096 = 4 KiB blocks
#
# IMPORTANT: no -w or -n flags are used.
# This is read-only.
badblocks -s -v -b 4096 "$DEV"
BADBLOCK_RESULT=$?
END_TIME=$(date +%s)
ELAPSED=$((END_TIME - START_TIME))
echo
echo "badblocks exit code: $BADBLOCK_RESULT"
printf "Physical scan duration: %02d:%02d:%02d\n" \
$((ELAPSED / 3600)) \
$(((ELAPSED % 3600) / 60)) \
$((ELAPSED % 60))
echo
echo "======================================================"
echo " 6. Looking for USB / SD / I/O errors in kernel log"
echo "======================================================"
dmesg --color=never |
grep -Ei \
'sdb|usb|uas|scsi|mmc|I/O error|buffer I/O|medium error|sense key|reset|timeout|CRC|failed command' |
tail -n 150 ||
true
echo
echo "======================================================"
echo " RESULTS"
echo "======================================================"
echo
printf "%-30s %s\n" "FAT boot filesystem:" "$FAT_RESULT"
printf "%-30s %s\n" "ext4 root filesystem:" "$EXT_RESULT"
printf "%-30s %s\n" "physical read scan:" "$BADBLOCK_RESULT"
echo
if [[ "$BADBLOCK_RESULT" -eq 0 ]]; then
echo "[PASS] Full physical card read completed."
else
echo "[FAIL] Physical read scan reported a problem."
fi
echo
if [[ "$FAT_RESULT" -eq 0 ]]; then
echo "[PASS] bootfs FAT filesystem appears clean."
else
echo "[WARN] bootfs filesystem reported errors or inconsistencies."
fi
if [[ "$EXT_RESULT" -eq 0 ]]; then
echo "[PASS] rootfs ext4 filesystem appears clean."
else
echo "[WARN] rootfs filesystem reported errors or inconsistencies."
fi
echo
echo "Log saved to:"
echo "$LOG"
if [[ -n "${SUDO_USER:-}" && "${SUDO_USER}" != "root" ]]; then
chown "$SUDO_USER":"$SUDO_USER" "$LOG" 2>/dev/null || true
fi
echo
echo "NO DATA WAS INTENTIONALLY WRITTEN TO THE MICROSD."