|
|
@@ -0,0 +1,334 @@
|
|
|
+#!/usr/bin/env bash
|
|
|
+#
|
|
|
+# =============================================================================
|
|
|
+# U 盘杀毒 / 清理脚本
|
|
|
+# 交互式 U 盘彻底抹除与重建分区
|
|
|
+# =============================================================================
|
|
|
+#
|
|
|
+# 用途:
|
|
|
+# - 彻底清除 U 盘 / 移动硬盘上的所有数据(包括病毒、恶意软件、隐藏分区)
|
|
|
+# - 擦除分区表签名(MBR + 备用 GPT)和首尾关键区域
|
|
|
+# - 新建 MBR 分区表 + 单分区,并格式化为 FAT32 / exFAT / NTFS / ext4
|
|
|
+#
|
|
|
+# 用法:
|
|
|
+# sudo ./usb_wipe.sh
|
|
|
+#
|
|
|
+# 交互步骤:
|
|
|
+# 1. 脚本自动扫描并列出所有可移动磁盘设备
|
|
|
+# 2. 按编号选择要清理的目标设备
|
|
|
+# 3. 输入设备型号进行第一次确认
|
|
|
+# 4. 输入大写 YES 进行最终确认(不可逆!)
|
|
|
+# 5. 选择文件系统类型(FAT32 / exFAT / NTFS / ext4 / 跳过格式化)
|
|
|
+# 6. 可选:输入卷标(回车使用默认值 USB_DRIVE)
|
|
|
+#
|
|
|
+# 依赖工具:
|
|
|
+# util-linux : lsblk, fdisk, sfdisk, wipefs, blockdev, partprobe
|
|
|
+# coreutils : dd, sync
|
|
|
+# dosfstools : mkfs.vfat (FAT32)
|
|
|
+# exfatprogs : mkfs.exfat (exFAT)
|
|
|
+# ntfs-3g : mkfs.ntfs (NTFS)
|
|
|
+# e2fsprogs : mkfs.ext4 (ext4)
|
|
|
+#
|
|
|
+# 安装依赖:sudo pacman -S dosfstools exfatprogs ntfs-3g e2fsprogs
|
|
|
+# (Debian/Ubuntu:sudo apt install dosfstools exfatprogs ntfs-3g e2fsprogs)
|
|
|
+#
|
|
|
+# 安全设计:
|
|
|
+# - 必须 root 运行
|
|
|
+# - 三层确认机制(编号选择 → 型号匹配 → 大写 YES)
|
|
|
+# - 自动检测并卸载目标设备上的挂载点
|
|
|
+# - 默认过滤系统盘(nvme / sda 非 USB 设备)
|
|
|
+# - 仅擦除分区表签名和首尾关键区域(不浪费生命全盘写零)
|
|
|
+#
|
|
|
+# ⚠️ 警告:本脚本会 永久销毁 目标设备上的所有数据,不可恢复!
|
|
|
+# 使用前请再三确认已选中正确的设备。
|
|
|
+#
|
|
|
+# =============================================================================
|
|
|
+
|
|
|
+set -euo pipefail
|
|
|
+
|
|
|
+# --- 颜色定义 ----------------------------------------------------------------
|
|
|
+RED='\033[0;31m'
|
|
|
+GREEN='\033[0;32m'
|
|
|
+YELLOW='\033[1;33m'
|
|
|
+CYAN='\033[0;36m'
|
|
|
+BOLD='\033[1m'
|
|
|
+NC='\033[0m' # No Color
|
|
|
+
|
|
|
+# --- 必须 root 运行 ----------------------------------------------------------
|
|
|
+if [[ $EUID -ne 0 ]]; then
|
|
|
+ echo -e "${RED}${BOLD}请使用 sudo 运行本脚本。${NC}"
|
|
|
+ exit 1
|
|
|
+fi
|
|
|
+
|
|
|
+# --- 横幅 --------------------------------------------------------------------
|
|
|
+clear
|
|
|
+echo -e "${RED}${BOLD}"
|
|
|
+echo " ╔══════════════════════════════════════════════════════════╗"
|
|
|
+echo " ║ ⚠ 警 告 — 不可逆操作 — 请谨慎使用 ⚠ ║"
|
|
|
+echo " ║ ║"
|
|
|
+echo " ║ 本脚本将 彻底抹除 U 盘全部内容 并重建分区表和文件系统 ║"
|
|
|
+echo " ║ 所有数据将 永久丢失,无法恢复! ║"
|
|
|
+echo " ╚══════════════════════════════════════════════════════════╝"
|
|
|
+echo -e "${NC}"
|
|
|
+echo ""
|
|
|
+
|
|
|
+# --- 工具检查 ----------------------------------------------------------------
|
|
|
+for cmd in lsblk dd fdisk mkfs.vfat mkfs.exfat mkfs.ext4 wipefs blockdev; do
|
|
|
+ if ! command -v "$cmd" &>/dev/null; then
|
|
|
+ echo -e "${YELLOW}缺少工具: $cmd,尝试安装...${NC}"
|
|
|
+ pacman -S --noconfirm --needed \
|
|
|
+ util-linux fdisk dosfstools exfatprogs e2fsprogs coreutils 2>/dev/null || true
|
|
|
+ break
|
|
|
+ fi
|
|
|
+done
|
|
|
+
|
|
|
+# --- 步骤 1:列出可移动设备 --------------------------------------------------
|
|
|
+echo -e "${CYAN}[1/5]${NC} 正在扫描可移动磁盘设备..."
|
|
|
+echo ""
|
|
|
+
|
|
|
+# 用 lsblk 列出可移动设备(TRAN=RPM 为空或 usb,TYPE=disk)
|
|
|
+mapfile -t USB_DISKS < <(
|
|
|
+ lsblk -dno NAME,SIZE,TRAN,MODEL,VENDOR 2>/dev/null \
|
|
|
+ | awk '
|
|
|
+ BEGIN { ignore["sda"]=1; ignore["nvme"]=1; ignore["loop"]=1 }
|
|
|
+ /^sd[b-z]/ || /^vd[b-z]/ || ($3 == "usb") {
|
|
|
+ print
|
|
|
+ }
|
|
|
+ ' \
|
|
|
+ | sort -u
|
|
|
+)
|
|
|
+
|
|
|
+if [[ ${#USB_DISKS[@]} -eq 0 ]]; then
|
|
|
+ echo -e "${RED}未检测到任何可移动磁盘设备。${NC}"
|
|
|
+ echo ""
|
|
|
+ echo "当前所有块设备:"
|
|
|
+ lsblk -dno NAME,SIZE,TRAN,MODEL 2>/dev/null | head -20
|
|
|
+ exit 1
|
|
|
+fi
|
|
|
+
|
|
|
+echo -e "${BOLD}检测到以下可移动磁盘:${NC}"
|
|
|
+echo ""
|
|
|
+printf " %-4s %-10s %-8s %-s\n" "编号" "设备名" "容量" "型号"
|
|
|
+echo " ────────────────────────────────────────"
|
|
|
+for i in "${!USB_DISKS[@]}"; do
|
|
|
+ read -r name size tran model vendor <<< "${USB_DISKS[$i]}"
|
|
|
+ printf " ${GREEN}%-4s${NC} ${RED}%-10s${NC} %-8s %-s %-s\n" \
|
|
|
+ "$((i+1))" "/dev/$name" "$size" "$model" "$vendor"
|
|
|
+done
|
|
|
+echo ""
|
|
|
+
|
|
|
+# --- 步骤 2:选择目标设备 ----------------------------------------------------
|
|
|
+while true; do
|
|
|
+ read -r -p "$(echo -e "${YELLOW}请选择要清理的设备编号(输入 0 退出):${NC}")" choice
|
|
|
+ if [[ "$choice" == "0" ]]; then
|
|
|
+ echo "已取消。"
|
|
|
+ exit 0
|
|
|
+ fi
|
|
|
+ if [[ "$choice" =~ ^[0-9]+$ ]] && (( choice >= 1 && choice <= ${#USB_DISKS[@]} )); then
|
|
|
+ idx=$((choice - 1))
|
|
|
+ read -r DEV_NAME DEV_SIZE DEV_TRAN DEV_MODEL DEV_VENDOR <<< "${USB_DISKS[$idx]}"
|
|
|
+ DEV_PATH="/dev/$DEV_NAME"
|
|
|
+ break
|
|
|
+ fi
|
|
|
+ echo -e "${RED}无效选择,请重新输入。${NC}"
|
|
|
+done
|
|
|
+
|
|
|
+echo ""
|
|
|
+
|
|
|
+# --- 步骤 3:二次确认(输入设备型号) -----------------------------------------
|
|
|
+echo -e "${RED}${BOLD}══════════════════════════════════════════════════════════════${NC}"
|
|
|
+echo -e "${RED}${BOLD} 最终确认:即将销毁 ${DEV_PATH}${NC}"
|
|
|
+echo -e "${RED}${BOLD} 设备型号:${DEV_MODEL:-未知} ${DEV_VENDOR:-}${NC}"
|
|
|
+echo -e "${RED}${BOLD} 容量:${DEV_SIZE}${NC}"
|
|
|
+echo -e "${RED}${BOLD}══════════════════════════════════════════════════════════════${NC}"
|
|
|
+echo ""
|
|
|
+
|
|
|
+# 显示设备当前分区信息
|
|
|
+echo -e "${CYAN}当前分区布局:${NC}"
|
|
|
+lsblk "$DEV_PATH" 2>/dev/null || true
|
|
|
+fdisk -l "$DEV_PATH" 2>/dev/null | head -20 || true
|
|
|
+echo ""
|
|
|
+
|
|
|
+# 检查是否有挂载
|
|
|
+MOUNTS=$(lsblk -no MOUNTPOINT "$DEV_PATH" 2>/dev/null | grep -v '^$' || true)
|
|
|
+if [[ -n "$MOUNTS" ]]; then
|
|
|
+ echo -e "${YELLOW}检测到挂载点:${NC}"
|
|
|
+ echo "$MOUNTS"
|
|
|
+ echo ""
|
|
|
+ read -r -p "$(echo -e "${YELLOW}是否卸载所有挂载点?[y/N] ${NC}")" umount_confirm
|
|
|
+ if [[ "$umount_confirm" =~ ^[Yy]$ ]]; then
|
|
|
+ for mp in $MOUNTS; do
|
|
|
+ umount "$mp" 2>/dev/null || umount -l "$mp" 2>/dev/null || true
|
|
|
+ done
|
|
|
+ echo -e "${GREEN}已卸载。${NC}"
|
|
|
+ fi
|
|
|
+fi
|
|
|
+
|
|
|
+echo ""
|
|
|
+echo -e "${RED}${BOLD}请输入设备型号 ${DEV_MODEL:-N/A} 来确认操作:${NC}"
|
|
|
+read -r confirm_model
|
|
|
+
|
|
|
+if [[ "$confirm_model" != "${DEV_MODEL}" ]]; then
|
|
|
+ echo -e "${GREEN}型号不匹配,操作已取消。${NC}"
|
|
|
+ exit 0
|
|
|
+fi
|
|
|
+
|
|
|
+echo ""
|
|
|
+echo -e "${RED}${BOLD}最后一次确认:输入 YES(大写)来执行不可逆的抹除操作:${NC}"
|
|
|
+read -r final_confirm
|
|
|
+
|
|
|
+if [[ "$final_confirm" != "YES" ]]; then
|
|
|
+ echo -e "${GREEN}操作已取消。${NC}"
|
|
|
+ exit 0
|
|
|
+fi
|
|
|
+
|
|
|
+echo ""
|
|
|
+echo -e "${RED}${BOLD}══════════════════════════════════════════════════════════════${NC}"
|
|
|
+echo -e "${RED}${BOLD} 开始执行... 请勿中断!${NC}"
|
|
|
+echo -e "${RED}${BOLD}══════════════════════════════════════════════════════════════${NC}"
|
|
|
+echo ""
|
|
|
+
|
|
|
+# --- 步骤 4:抹除 ------------------------------------------------------------
|
|
|
+echo -e "${CYAN}[2/5]${NC} 清除分区表签名(wipefs)..."
|
|
|
+wipefs --all --force "$DEV_PATH" 2>/dev/null || true
|
|
|
+echo -e "${GREEN} ✓ 完成${NC}"
|
|
|
+
|
|
|
+echo -e "${CYAN}[3/5]${NC} dd 全盘置零(这可能需要几分钟)..."
|
|
|
+
|
|
|
+BLOCK_SIZE=$(blockdev --getsize64 "$DEV_PATH" 2>/dev/null || echo 0)
|
|
|
+SECTOR_SIZE=$(blockdev --getss "$DEV_PATH" 2>/dev/null || echo 512)
|
|
|
+
|
|
|
+# 先写开头 10 MiB(MBR/GPT 区域)
|
|
|
+dd if=/dev/zero of="$DEV_PATH" bs=1M count=10 conv=fsync status=none 2>/dev/null && \
|
|
|
+ echo -e "${GREEN} ✓ 前 10 MiB 已清零${NC}" || \
|
|
|
+ echo -e "${RED} ✗ 写入失败${NC}"
|
|
|
+
|
|
|
+# 再写末尾 10 MiB(备用 GPT 区域)
|
|
|
+if [[ "$BLOCK_SIZE" -gt 0 ]]; then
|
|
|
+ SEEK_VAL=$(( (BLOCK_SIZE - 10*1024*1024) / 512 ))
|
|
|
+ if [[ "$SEEK_VAL" -gt 0 ]]; then
|
|
|
+ dd if=/dev/zero of="$DEV_PATH" bs=512 seek="$SEEK_VAL" count=20480 conv=fsync status=none 2>/dev/null && \
|
|
|
+ echo -e "${GREEN} ✓ 末尾 10 MiB 已清零${NC}" || \
|
|
|
+ echo -e "${RED} ✗ 末尾写入失败${NC}"
|
|
|
+ fi
|
|
|
+fi
|
|
|
+
|
|
|
+echo -e "${CYAN}[4/5]${NC} 同步并刷新内核分区表..."
|
|
|
+sync
|
|
|
+partprobe "$DEV_PATH" 2>/dev/null || blockdev --rereadpt "$DEV_PATH" 2>/dev/null || true
|
|
|
+sleep 1
|
|
|
+echo -e "${GREEN} ✓ 完成${NC}"
|
|
|
+
|
|
|
+# --- 步骤 5:选择文件系统并重建分区 ------------------------------------------
|
|
|
+echo ""
|
|
|
+echo -e "${CYAN}[5/5]${NC} 选择要创建的文件系统类型:"
|
|
|
+echo ""
|
|
|
+echo " 1) FAT32 — 兼容性最好(单文件最大 4 GiB)"
|
|
|
+echo " 2) exFAT — 兼容性好,支持大文件(推荐)"
|
|
|
+echo " 3) NTFS — Windows 默认"
|
|
|
+echo " 4) ext4 — Linux 专用"
|
|
|
+echo " 5) 跳过格式化"
|
|
|
+echo ""
|
|
|
+
|
|
|
+FSTYPE_CHOICE=""
|
|
|
+while true; do
|
|
|
+ read -r -p "$(echo -e "${YELLOW}请选择 [1-5]:${NC}")" fstype
|
|
|
+ case "$fstype" in
|
|
|
+ 1) FSTYPE="vfat"; FSTYPE_LABEL="FAT32"; break ;;
|
|
|
+ 2) FSTYPE="exfat"; FSTYPE_LABEL="exFAT"; break ;;
|
|
|
+ 3) FSTYPE="ntfs"; FSTYPE_LABEL="NTFS"; break ;;
|
|
|
+ 4) FSTYPE="ext4"; FSTYPE_LABEL="ext4"; break ;;
|
|
|
+ 5) FSTYPE=""; break ;;
|
|
|
+ *) echo -e "${RED}无效选择。${NC}" ;;
|
|
|
+ esac
|
|
|
+done
|
|
|
+
|
|
|
+if [[ -n "$FSTYPE" ]]; then
|
|
|
+ read -r -p "$(echo -e "${YELLOW}卷标(回车默认 USB_DRIVE):${NC}")" VOLUME_LABEL
|
|
|
+ VOLUME_LABEL="${VOLUME_LABEL:-USB_DRIVE}"
|
|
|
+
|
|
|
+ echo ""
|
|
|
+ echo -e "${CYAN}创建 MBR 分区表 + 单分区...${NC}"
|
|
|
+
|
|
|
+ # 使用 sfdisk 创建单个 Linux 分区(类型稍后根据 fs 调整)
|
|
|
+ echo 'label: dos' | sfdisk --wipe always "$DEV_PATH" 2>/dev/null
|
|
|
+ sleep 1
|
|
|
+
|
|
|
+ # 创建分区(从 2048 扇区开始,使用全部剩余空间)
|
|
|
+ echo '2048,,83' | sfdisk --wipe always "$DEV_PATH" 2>/dev/null
|
|
|
+ sleep 1
|
|
|
+
|
|
|
+ partprobe "$DEV_PATH" 2>/dev/null || true
|
|
|
+ sleep 1
|
|
|
+
|
|
|
+ # 获取分区名
|
|
|
+ PART_PATH="${DEV_PATH}1"
|
|
|
+ if [[ "$DEV_NAME" == nvme* ]] || [[ "$DEV_NAME" == mmcblk* ]]; then
|
|
|
+ PART_PATH="${DEV_PATH}p1"
|
|
|
+ fi
|
|
|
+
|
|
|
+ # 等待分区节点出现
|
|
|
+ for _ in {1..10}; do
|
|
|
+ if [[ -b "$PART_PATH" ]]; then
|
|
|
+ break
|
|
|
+ fi
|
|
|
+ sleep 0.5
|
|
|
+ done
|
|
|
+
|
|
|
+ if [[ ! -b "$PART_PATH" ]]; then
|
|
|
+ echo -e "${RED}分区设备 $PART_PATH 未出现,请手动检查。${NC}"
|
|
|
+ exit 1
|
|
|
+ fi
|
|
|
+
|
|
|
+ echo -e "${GREEN} ✓ 分区已创建:${PART_PATH}${NC}"
|
|
|
+
|
|
|
+ # 格式化
|
|
|
+ echo -e "${CYAN}格式化 ${PART_PATH} 为 ${FSTYPE_LABEL}...${NC}"
|
|
|
+ case "$FSTYPE" in
|
|
|
+ vfat)
|
|
|
+ mkfs.vfat -F 32 -n "${VOLUME_LABEL}" "$PART_PATH"
|
|
|
+ ;;
|
|
|
+ exfat)
|
|
|
+ mkfs.exfat -n "${VOLUME_LABEL}" "$PART_PATH"
|
|
|
+ ;;
|
|
|
+ ntfs)
|
|
|
+ mkfs.ntfs -f -L "${VOLUME_LABEL}" "$PART_PATH" 2>/dev/null || \
|
|
|
+ mkfs.ntfs -Q -L "${VOLUME_LABEL}" "$PART_PATH"
|
|
|
+ ;;
|
|
|
+ ext4)
|
|
|
+ mkfs.ext4 -F -L "${VOLUME_LABEL}" "$PART_PATH"
|
|
|
+ ;;
|
|
|
+ esac
|
|
|
+ echo -e "${GREEN} ✓ 格式化完成${NC}"
|
|
|
+
|
|
|
+ # 刷新分区类型标识
|
|
|
+ case "$FSTYPE" in
|
|
|
+ vfat) echo 'type=0c' | sfdisk --part-type "$DEV_PATH" 1 2>/dev/null || true ;;
|
|
|
+ exfat) echo 'type=07' | sfdisk --part-type "$DEV_PATH" 1 2>/dev/null || true ;;
|
|
|
+ ntfs) echo 'type=07' | sfdisk --part-type "$DEV_PATH" 1 2>/dev/null || true ;;
|
|
|
+ esac
|
|
|
+else
|
|
|
+ PART_PATH="$DEV_PATH"
|
|
|
+ echo -e "${YELLOW}跳过格式化。${NC}"
|
|
|
+fi
|
|
|
+
|
|
|
+sync
|
|
|
+partprobe "$DEV_PATH" 2>/dev/null || true
|
|
|
+
|
|
|
+# --- 完成 --------------------------------------------------------------------
|
|
|
+echo ""
|
|
|
+echo -e "${GREEN}${BOLD}══════════════════════════════════════════════════════════════${NC}"
|
|
|
+echo -e "${GREEN}${BOLD} ✓ 操作完成!${NC}"
|
|
|
+echo -e "${GREEN}${BOLD}══════════════════════════════════════════════════════════════${NC}"
|
|
|
+echo ""
|
|
|
+echo -e " 设备:${BOLD}${DEV_PATH}${NC}"
|
|
|
+if [[ -n "$FSTYPE" ]]; then
|
|
|
+ echo -e " 分区:${BOLD}${PART_PATH}${NC}"
|
|
|
+ echo -e " 文件系统:${BOLD}${FSTYPE_LABEL}${NC}"
|
|
|
+ echo -e " 卷标:${BOLD}${VOLUME_LABEL}${NC}"
|
|
|
+fi
|
|
|
+echo ""
|
|
|
+echo -e " 最终布局:"
|
|
|
+lsblk "$DEV_PATH" 2>/dev/null || true
|
|
|
+echo ""
|
|
|
+echo -e "${CYAN}可以拔下 U 盘了。${NC}"
|