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