ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

ubuntu26.04坏道坏块分区隔离急速版260831-V0.12

ubuntu26.04坏道坏块分区隔离急速版260831-V0.12 效果不错最后提醒一下做分区之前一定要设置自定义磁盘备份分区错了也不用怕 dd ifimg of/dev/sdx bs512 count8备份分区的原理就是前面4K一个扇区512,1K是2个扇区4k是8个扇区也就是常说的4klinux 格式化ext4 mkfs.ext4 -b 4096 /dev/sde1 加上这个统一blk大小日后方便维护说明文件10:38 2026-8-30总共4个文件1对整个磁盘扫描出现坏块就跳过指定大小区域继续扫描保存结果2读取结果文件根据指定间隔大小输出符合规则的区域信息到文件4读取分区结果根据分区区域内扫描坏块文件继续保存到结果如果还有坏道坏块转到2 然后4如果没坏道坏块到3分区结束3读取分区信息文件格式化磁盘gpt分区隔离避开坏道坏块启动顺序流程第一次执行sudo python3 1-chk_disk_skip_500M.py循环2 4 流程一直到不再出现坏道坏块sudo python3 2-get_part_to_new_file_test4.pysudo python3 4-scan_bad_blocks.py上面不再出现坏道坏块继续下面备份分区表sudo python3 3-gpt-part.py第一次执行这个清除所有分区并分区sudo python3 3.1-v0.10_auto_backup_pt.py备份分区表sudo python3 3.1-v0.10_auto_backup_pt.py备份分区表sudo python3 3.1-v0.10_auto_backup_pt.py以后可以使用不清除分区并分区sudo python3 3.1-v0.10-gpt-part-no-delte-all.py备份分区表sudo python3 3.1-v0.10_auto_backup_pt.pyupdate_logV0.11-260830第二个版本新增备份全部分区的功能防止错误选择磁盘导致问题sudo python3 3.1-v0.10_auto_backup_pt.py#百度关键提示词词生成代码#python 用linux的dd命令导出 所有磁盘 分区表 根据日期时间和磁盘名称 到文件#能否提供一个更简洁的版本改成日期加时间的格式 保存在日期加格式的文件夹里面 把备份大小改成4096改成日期加时间的格式 保存在日期加格式精确到秒的文件夹里面 把备份大小改成40963.1-v0.10_auto_backup_pt.py#自述本人只是想到的结合百度ai生成的缝合怪专业的高手不要笑话就好。很多结构和逻辑经不起推敲只是缝合一起按流程走通了#有能力整合的希望联系我一起优化一下这缝合怪#陆陆续续硬盘多多少少坏道就出现了但是还能分区格式化#就突发奇想怎么利用起来临时存储也可以例如下载临时文件存放游戏客户端。#为了这点空间如果手动处理肯定不划算还不如买新的所以出现了缝合怪版本几个步骤就自动解决隔离坏道坏块问题下载地址https://download.csdn.net/download/cyuyan112233/93366615不知道你们能下载么上面的下面我贴上代码吧不用费劲了sudo python3 1-v0.12-chk_disk_skip_500M.pyimport os import sys import time import struct import fcntl import subprocess # --- 配置参数 --- DEVICE_PATH /dev/sda # 目标设备路径请根据实际情况修改 OUTPUT_FILE found_bad_blocks.txt # 坏块记录文件 BLOCK_SIZE 4096 # 块大小 (字节) SKIP_SIZE_MB 500 # 发现坏块后跳过的距离 (MB) ALL_current_block 91450000 #自定义当前块 # 计算跳跃的块数 SKIP_BLOCKS (SKIP_SIZE_MB * 1024 * 1024) // BLOCK_SIZE def get_block_device_size(device_path): 获取块设备大小的稳健方法 优先使用 os.path.getsize失败则尝试 ioctl最后尝试 blockdev 命令 # 方法 1: 使用 os.path.getsize (现代 Linux 内核通常支持) try: size os.path.getsize(device_path) if size 0: return size except Exception as e: print(fos.path.getsize 尝试失败: {e}) # 方法 2: 使用 ioctl BLKGETSIZE64 try: # O_NONBLOCK 防止在某些特殊设备上挂起 fd os.open(device_path, os.O_RDONLY | os.O_NONBLOCK) try: # BLKGETSIZE64 请求码: 0x80081272 # 缓冲区必须是 8 字节 buf b\x00 * 8 ret_buf fcntl.ioctl(fd, 0x80081272, buf) # Q 表示小端序 unsigned long long (8 bytes) size struct.unpack(Q, ret_buf) if size 0: return size except Exception as e: print(fioctl 尝试失败: {e}) finally: os.close(fd) except Exception as e: print(f打开设备文件失败: {e}) # 方法 3: 使用 blockdev 命令作为最后的手段 try: result subprocess.run( [blockdev, --getsize64, device_path], stdoutsubprocess.PIPE, stderrsubprocess.PIPE, textTrue, timeout5 ) if result.returncode 0: return int(result.stdout.strip()) except Exception as e: print(fblockdev 命令执行失败: {e}) return 0 def check_block_readability(device_path, block_index): 尝试读取指定索引的块。 如果成功返回 True如果失败IOError/OSError返回 False。 try: with open(device_path, rb) as f: f.seek(block_index * BLOCK_SIZE) data f.read(BLOCK_SIZE) if len(data) BLOCK_SIZE: return False return True except OSError: return False except Exception: return False def scan_device(): bad_blocks [] # 获取设备大小 print(f正在检测设备 {DEVICE_PATH} ...) device_size get_block_device_size(DEVICE_PATH) if device_size 0: print(f错误: 无法获取设备 {DEVICE_PATH} 的大小。) print(请执行以下检查:) print(f1. 运行 lsblk {DEVICE_PATH} 确认设备存在且显示大小。) print(f2. 运行 sudo blockdev --getsize64 {DEVICE_PATH} 确认内核能识别大小。) print(3. 确保设备未处于休眠状态且连接正常。) return total_blocks device_size // BLOCK_SIZE print(f设备: {DEVICE_PATH}) print(f总大小: {device_size / (1024**3):.2f} GB) print(f总块数 (4K): {total_blocks}) print(f策略: 遇到坏块跳过 {SKIP_SIZE_MB} MB ({SKIP_BLOCKS} blocks)) print(- * 30) #更改当前进度 current_block ALL_current_block #6790000 start_time time.time() try: # 以只读、二进制模式打开 with open(DEVICE_PATH, rb) as f: while current_block total_blocks: # 进度显示 if current_block % 10000 0: progress (current_block / total_blocks) * 100 elapsed time.time() - start_time speed current_block / elapsed if elapsed 0 else 0 sys.stdout.write(f\r扫描进度: {progress:.2f}% | 当前块: {current_block} | 速度: {speed:.0f} blocks/s) sys.stdout.flush() # 检测当前块 if not check_block_readability(DEVICE_PATH, current_block): print(f\n[!] 发现坏块 at Block Index: {current_block} (Offset: {current_block * BLOCK_SIZE})) bad_blocks.append(current_block) # 记录到文件 with open(OUTPUT_FILE, a) as out_f: out_f.write(f{current_block}\n) print(f[-] 跳过 {SKIP_SIZE_MB} MB...) current_block SKIP_BLOCKS else: current_block 1 print(\n扫描完成。) print(f共发现 {len(bad_blocks)} 个坏块区域。) # 记录到文件 with open(OUTPUT_FILE, a) as out_f: out_f.write(f{total_blocks}\n) print(f结果已保存至: {OUTPUT_FILE}) except PermissionError: print(错误: 权限不足。请使用 sudo 运行此脚本。) except FileNotFoundError: print(f错误: 设备 {DEVICE_PATH} 不存在。) except Exception as e: print(f发生未知错误: {e}) if __name__ __main__: if os.geteuid() ! 0: print(警告: 建议以 root 权限运行以访问原始设备。) # 不清空旧结果 #if os.path.exists(OUTPUT_FILE): #os.remove(OUTPUT_FILE) scan_device()sudo python3 2-get_part_to_new_file_test4.py#!/usr/bin/env python3 import os import sys #V0.11-20260830新增功能排序 #V0.10-20260829第一个版本 # 配置常量 INPUT_FILE found_bad_blocks.txt OUTPUT_FILE large_gap_bad_blocks.txt # 导出的新文件路径 BLOCK_SIZE 4096 # 块大小4096 字节 THRESHOLD_MB 512 * 3 100 # 间隔阈值1 MB 自定义 #V0.11-20260830新增功能排序 input_pathINPUT_FILE output_pathINPUT_FILE #排序功能 def read_and_sort_bad_blocks(input_path, output_path): 读取坏块文件去重排序并写入到新文件 返回排序后的整数列表 bad_blocks set() # 1. 读取文件 if not os.path.exists(input_path): print(f错误: 输入文件 {input_path} 不存在) sys.exit(1) try: with open(input_path, r) as f: for line in f: line line.strip() if line: try: # 尝试转换为整数忽略非数字行 bad_blocks.add(int(line)) except ValueError: print(f警告: 跳过无效行 {line}) except Exception as e: print(f读取文件失败: {e}) sys.exit(1) # 2. 排序 sorted_blocks sorted(list(bad_blocks)) # 3. 写入排序后的文件 try: with open(output_path, w) as f: for block in sorted_blocks: f.write(f{block}\n) print(f已排序并保存至: {output_path}) print(f共发现 {len(sorted_blocks)} 个坏块。) except Exception as e: print(f写入文件失败: {e}) sys.exit(1) return sorted_blocks def export_large_gap_blocks(input_path, output_path, block_size, threshold_mb): 读取排序后的坏块文件找出间隔超过阈值的坏块对并导出到新文件。 if not os.path.exists(input_path): print(f错误: 输入文件 {input_path} 不存在) return # 计算阈值对应的字节数 threshold_bytes threshold_mb * 1024 * 1024 prev_block None line_count 0 exported_count 0 print(f正在处理文件: {input_path}) print(f阈值设置: 间隔 {threshold_mb} MB) try: # 以写入模式打开输出文件 with open(input_path, r) as infile, open(output_path, w) as outfile: for line in infile: line line.strip() if not line: continue try: current_block int(line) except ValueError: continue line_count 1 # 如果不是第一行计算间隔 if prev_block is not None: gap_bytes (current_block - prev_block) * block_size # 如果间隔大于阈值将这两个坏块号写入新文件 if gap_bytes threshold_bytes: outfile.write(f{prev_block}\n) outfile.write(f{current_block}\n) exported_count 2 # 每次写入两个坏块号 # 更新上一个坏块 prev_block current_block print(f处理完成。) print(f总读取坏块数: {line_count}) print(f导出的坏块数: {exported_count}) print(f结果已保存至: {output_path}) except Exception as e: print(f发生错误: {e}) sys.exit(1) def main(): print(f正在处理坏块文件: {INPUT_FILE}) read_and_sort_bad_blocks(input_path, output_path) export_large_gap_blocks(INPUT_FILE, OUTPUT_FILE, BLOCK_SIZE, THRESHOLD_MB) if __name__ __main__: main()sudo python3 3.1-v0.10_auto_backup_pt.pyimport subprocess import os import glob from datetime import datetime def get_disks(): 快速获取所有物理磁盘名称 disks [] try: # 使用 lsblk 获取类型为 disk 的设备名 res subprocess.run([lsblk, -d, -n, -o, NAME,TYPE], capture_outputTrue, textTrue, checkTrue) for line in res.stdout.splitlines(): if disk in line: disks.append(line.split()[0]) except Exception: # fallback: 常见物理设备模式 disks [os.path.basename(p) for p in glob.glob(/dev/sd[a-z]) glob.glob(/dev/nvme[0-9]*n[0-9]*)] return disks def backup_disk(dev_name, base_dirbackups): 备份单个磁盘的分区表 (前4096字节) 保存到: base_dir/YYYYMMDD_HHMMSS/dev_name_pt_YYYYMMDD_HHMMSS.img # 生成时间戳 timestamp datetime.now().strftime(%Y%m%d_%H%M%S) # 构建带日期的文件夹路径 folder_path os.path.join(base_dir, timestamp) os.makedirs(folder_path, exist_okTrue) src f/dev/{dev_name} # 构建文件名 filename f{dev_name}_pt_{timestamp}.img dst os.path.join(folder_path, filename) if not os.path.exists(src): print(f[SKIP] {src} 不存在) return False try: # dd 命令: # bs4096: 块大小 4096 字节 # count1: 只拷贝 1 个块 (总共 4096 字节) # statusnone: 静默执行不输出进度 subprocess.run([dd, fif{src}, fof{dst}, bs4096, count1, statusnone], checkTrue, capture_outputTrue) # 验证文件大小 if os.path.exists(dst) and os.path.getsize(dst) 4096: print(f[OK] {dev_name} - {dst} (4096 bytes)) return True else: print(f[WARN] {dev_name} 备份完成但大小校验异常) return True except Exception as e: print(f[ERR] {dev_name}: {e}) return False if __name__ __main__: if os.geteuid() ! 0: print(错误: 请使用 sudo 运行) exit(1) current_time_str datetime.now().strftime(%Y-%m-%d %H:%M:%S) print(f开始备份 ({current_time_str})...) print(f备份策略: 每个磁盘前 4096 字节 (MBR/GPT Header)) disks get_disks() if not disks: print(未检测到物理磁盘。) exit(0) success_count 0 for disk in disks: if backup_disk(disk): success_count 1 print(f完成。成功备份 {success_count}/{len(disks)} 个磁盘。)sudo python3 3.1-v0.10-gpt-part-no-delte-all.py#!/usr/bin/env python3 import os import sys import struct import subprocess import time #V0.10-260830 去除清除全部分区直接建立指定分区 # --- 配置参数 --- #!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! DEVICE /dev/sda # 目标磁盘设备请根据实际情况修改 INPUT_FILE large_gap_bad_blocks.txt # 坏块/分区定义文件 BLOCK_SIZE 4096 # 逻辑块大小 (字节) SECTOR_SIZE 512 # 物理扇区大小 (字节) SGDISK_CMD sgdisk # GPT分区工具 # 1M 256 blk # 262,144 1G对应的blk # THRESHOLD_MB 512 * 3 100 # 间隔阈值1 MB ##1G有效空间两边空余50M ALL_start_blk_add_blk256 * (512 50) ALL_end_blk_xor_blk256 * 50 def check_root(): if os.geteuid() ! 0: print(错误: 此脚本需要 root 权限 (sudo) 才能操作原始磁盘设备。) sys.exit(1) def check_dependencies(): if not subprocess.run([which, SGDISK_CMD], stdoutsubprocess.PIPE, stderrsubprocess.PIPE).returncode 0: print(f错误: 未找到命令 {SGDISK_CMD}。) print(请安装 gdisk 包: sudo apt-get install gdisk) sys.exit(1) def read_partition_definitions(filepath): 读取文件每两行为一组起始块, 结束块 返回: [(start_block, end_block), ...] if not os.path.exists(filepath): print(f错误: 文件 {filepath} 不存在。) sys.exit(1) pairs [] with open(filepath, r) as f: lines [line.strip() for line in f if line.strip()] if len(lines) % 2 ! 0: print(错误: 输入文件格式错误。行数必须为偶数每两行定义一个分区起始块, 结束块。) sys.exit(1) for i in range(0, len(lines), 2): try: start_blk int(lines[i]) ALL_start_blk_add_blk end_blk int(lines[i1]) - ALL_end_blk_xor_blk if start_blk 0 or end_blk 0: raise ValueError(块索引不能为负数) if start_blk end_blk: print(f警告: 跳过无效区间 (Start {start_blk} End {end_blk})) continue pairs.append((start_blk, end_blk)) except ValueError as e: print(f错误: 无法解析行 {lines[i]} 或 {lines[i1]}。{e}) sys.exit(1) if not pairs: print(错误: 文件中没有有效的分区定义。) sys.exit(1) return pairs def wipe_disk(device): 清除磁盘现有的分区表 (MBR/GPT) print(f[步骤 1/3] 正在清除 {device} 上的现有分区表...) try: # -Z: zap (destroy) GPT and MBR data structures result subprocess.run( [SGDISK_CMD, -Z, device], stdoutsubprocess.PIPE, stderrsubprocess.PIPE, textTrue ) if result.returncode ! 0: # 有时如果磁盘全是0sgdisk可能会报错说没发现有效签名这通常是可以接受的 if No valid GPT or MBR in result.stderr or No partition table found in result.stderr: print( 磁盘似乎已经是空的或无有效分区表。) else: print(f 清除失败: {result.stderr}) return False else: print( 分区表已清除。) # 强制内核重新读取分区表 subprocess.run([partprobe, device], stdoutsubprocess.DEVNULL, stderrsubprocess.DEVNULL) time.sleep(1) return True except Exception as e: print(f 执行清除命令时出错: {e}) return False def create_partitions(device, partitions): 使用 sgdisk 创建分区 注意: sgdisk 使用 512 字节扇区作为单位。 转换: 扇区索引 块索引 * (BLOCK_SIZE / SECTOR_SIZE) multiplier BLOCK_SIZE // SECTOR_SIZE # 通常为 8 print(f[步骤 2/3] 开始在 {device} 上创建 {len(partitions)} 个分区...) for idx, (start_blk, end_blk) in enumerate(partitions, 1): start_sec start_blk * multiplier end_sec end_blk * multiplier # sgdisk 分区号从 1 开始 part_num idx print(f - 分区 {part_num}: 块 [{start_blk}-{end_blk}] 扇区 [{start_sec}-{end_sec}]) try: # -n: new partition # 格式: -n part_num:start_sector:end_sector cmd [SGDISK_CMD, -n, f{part_num}:{start_sec}:{end_sec}, device] result subprocess.run( cmd, stdoutsubprocess.PIPE, stderrsubprocess.PIPE, textTrue ) if result.returncode ! 0: print(f [失败] {result.stderr.strip()}) return False else: print(f [成功]) except Exception as e: print(f [错误] {e}) return False return True def verify_partitions(device): 验证并打印最终分区表 print(f[步骤 3/3] 验证分区表...) try: result subprocess.run( [SGDISK_CMD, -p, device], stdoutsubprocess.PIPE, stderrsubprocess.PIPE, textTrue ) if result.returncode 0: print(\n--- 当前磁盘分区结构 ---) print(result.stdout) print(------------------------) return True else: print(f验证失败: {result.stderr}) return False except Exception as e: print(f验证时出错: {e}) return False def main(): print(*50) print(GPT 自动分区工具) print(*50) check_root() check_dependencies() print(f目标设备: {DEVICE}) print(f定义文件: {INPUT_FILE}) print(f块大小: {BLOCK_SIZE} Bytes) print(-*50) # 1. 读取定义 partitions read_partition_definitions(INPUT_FILE) print(f已加载 {len(partitions)} 个分区定义。) # 确认操作 print(\n警告: 此操作将清除磁盘所有数据并重建分区表) confirm input(请输入 yes 继续: ) if confirm.lower() ! yes: print(操作已取消。) sys.exit(0) # 2. 清除磁盘 # if not wipe_disk(DEVICE): # print( aborting due to wipe failure.) # sys.exit(1) # 3. 创建分区 if not create_partitions(DEVICE, partitions): print( aborting due to partition creation failure.) sys.exit(1) # 4. 验证 verify_partitions(DEVICE) print(\n所有操作完成。) if __name__ __main__: main()sudo python3 3-gpt-part.py#!/usr/bin/env python3 import os import sys import struct import subprocess import time # --- 配置参数 --- DEVICE /dev/sda # 目标磁盘设备请根据实际情况修改 INPUT_FILE large_gap_bad_blocks.txt # 坏块/分区定义文件 BLOCK_SIZE 4096 # 逻辑块大小 (字节) SECTOR_SIZE 512 # 物理扇区大小 (字节) SGDISK_CMD sgdisk # GPT分区工具 # 1M 256 blk # 262,144 1G对应的blk # THRESHOLD_MB 512 * 3 100 # 间隔阈值1 MB ##1G有效空间两边空余50M ALL_start_blk_add_blk256 * (512 50) ALL_end_blk_xor_blk256 * 50 def check_root(): if os.geteuid() ! 0: print(错误: 此脚本需要 root 权限 (sudo) 才能操作原始磁盘设备。) sys.exit(1) def check_dependencies(): if not subprocess.run([which, SGDISK_CMD], stdoutsubprocess.PIPE, stderrsubprocess.PIPE).returncode 0: print(f错误: 未找到命令 {SGDISK_CMD}。) print(请安装 gdisk 包: sudo apt-get install gdisk) sys.exit(1) def read_partition_definitions(filepath): 读取文件每两行为一组起始块, 结束块 返回: [(start_block, end_block), ...] if not os.path.exists(filepath): print(f错误: 文件 {filepath} 不存在。) sys.exit(1) pairs [] with open(filepath, r) as f: lines [line.strip() for line in f if line.strip()] if len(lines) % 2 ! 0: print(错误: 输入文件格式错误。行数必须为偶数每两行定义一个分区起始块, 结束块。) sys.exit(1) for i in range(0, len(lines), 2): try: start_blk int(lines[i]) ALL_start_blk_add_blk end_blk int(lines[i1]) - ALL_end_blk_xor_blk if start_blk 0 or end_blk 0: raise ValueError(块索引不能为负数) if start_blk end_blk: print(f警告: 跳过无效区间 (Start {start_blk} End {end_blk})) continue pairs.append((start_blk, end_blk)) except ValueError as e: print(f错误: 无法解析行 {lines[i]} 或 {lines[i1]}。{e}) sys.exit(1) if not pairs: print(错误: 文件中没有有效的分区定义。) sys.exit(1) return pairs def wipe_disk(device): 清除磁盘现有的分区表 (MBR/GPT) print(f[步骤 1/3] 正在清除 {device} 上的现有分区表...) try: # -Z: zap (destroy) GPT and MBR data structures result subprocess.run( [SGDISK_CMD, -Z, device], stdoutsubprocess.PIPE, stderrsubprocess.PIPE, textTrue ) if result.returncode ! 0: # 有时如果磁盘全是0sgdisk可能会报错说没发现有效签名这通常是可以接受的 if No valid GPT or MBR in result.stderr or No partition table found in result.stderr: print( 磁盘似乎已经是空的或无有效分区表。) else: print(f 清除失败: {result.stderr}) return False else: print( 分区表已清除。) # 强制内核重新读取分区表 subprocess.run([partprobe, device], stdoutsubprocess.DEVNULL, stderrsubprocess.DEVNULL) time.sleep(1) return True except Exception as e: print(f 执行清除命令时出错: {e}) return False def create_partitions(device, partitions): 使用 sgdisk 创建分区 注意: sgdisk 使用 512 字节扇区作为单位。 转换: 扇区索引 块索引 * (BLOCK_SIZE / SECTOR_SIZE) multiplier BLOCK_SIZE // SECTOR_SIZE # 通常为 8 print(f[步骤 2/3] 开始在 {device} 上创建 {len(partitions)} 个分区...) for idx, (start_blk, end_blk) in enumerate(partitions, 1): start_sec start_blk * multiplier end_sec end_blk * multiplier # sgdisk 分区号从 1 开始 part_num idx print(f - 分区 {part_num}: 块 [{start_blk}-{end_blk}] 扇区 [{start_sec}-{end_sec}]) try: # -n: new partition # 格式: -n part_num:start_sector:end_sector cmd [SGDISK_CMD, -n, f{part_num}:{start_sec}:{end_sec}, device] result subprocess.run( cmd, stdoutsubprocess.PIPE, stderrsubprocess.PIPE, textTrue ) if result.returncode ! 0: print(f [失败] {result.stderr.strip()}) return False else: print(f [成功]) except Exception as e: print(f [错误] {e}) return False return True def verify_partitions(device): 验证并打印最终分区表 print(f[步骤 3/3] 验证分区表...) try: result subprocess.run( [SGDISK_CMD, -p, device], stdoutsubprocess.PIPE, stderrsubprocess.PIPE, textTrue ) if result.returncode 0: print(\n--- 当前磁盘分区结构 ---) print(result.stdout) print(------------------------) return True else: print(f验证失败: {result.stderr}) return False except Exception as e: print(f验证时出错: {e}) return False def main(): print(*50) print(GPT 自动分区工具) print(*50) check_root() check_dependencies() print(f目标设备: {DEVICE}) print(f定义文件: {INPUT_FILE}) print(f块大小: {BLOCK_SIZE} Bytes) print(-*50) # 1. 读取定义 partitions read_partition_definitions(INPUT_FILE) print(f已加载 {len(partitions)} 个分区定义。) # 确认操作 print(\n警告: 此操作将清除磁盘所有数据并重建分区表) confirm input(请输入 yes 继续: ) if confirm.lower() ! yes: print(操作已取消。) sys.exit(0) # 2. 清除磁盘 if not wipe_disk(DEVICE): print( aborting due to wipe failure.) sys.exit(1) # 3. 创建分区 if not create_partitions(DEVICE, partitions): print( aborting due to partition creation failure.) sys.exit(1) # 4. 验证 verify_partitions(DEVICE) print(\n所有操作完成。) if __name__ __main__: main()sudo python3 4-scan_bad_blocks.pyimport os import sys import time #export TARGET_DEV/dev/sdb #sudo python3 scan_bad_blocks.py TARGET_DEV/dev/sdb # --- 配置参数 --- INPUT_FILE large_gap_bad_blocks.txt OUTPUT_FILE found_bad_blocks.txt BLOCK_SIZE 4096 SKIP_MB 500 # 计算跳跃的块数: 500 * 1024 * 1024 / 4096 SKIP_BLOCKS (SKIP_MB * 1024 * 1024) // BLOCK_SIZE # 目标设备路径 (建议通过环境变量传入例如: export TARGET_DEV/dev/sdb) TARGET_DEV os.environ.get(TARGET_DEV, /dev/sdb) def log_message(msg): print(f[{time.strftime(%H:%M:%S)}] {msg}) def read_ranges(filepath): 读取分区范围文件返回 [(start, end), ...] ranges [] if not os.path.exists(filepath): log_message(f错误: 文件 {filepath} 不存在) return ranges with open(filepath, r) as f: lines [line.strip() for line in f if line.strip()] if len(lines) % 2 ! 0: log_message(警告: 输入文件行数不是偶数最后一行将被忽略) lines lines[:-1] for i in range(0, len(lines), 2): try: start int(lines[i]) end int(lines[i1]) if start end: ranges.append((start, end)) else: log_message(f跳过无效范围: {start} - {end}) except ValueError: log_message(f跳过非数字行: {lines[i]}, {lines[i1]}) return ranges def scan_device(dev_path, start_blk, end_blk, out_f): 在指定块范围内扫描坏块 current_blk start_blk bad_count 0 # 进度统计 total_blks end_blk - start_blk if total_blks 0: return 0 try: # 以二进制只读方式打开原始设备 with open(dev_path, rb) as f: while current_blk end_blk: # 简单的进度显示 (每 10000 块或每次跳跃时打印) if current_blk % 10000 0 or current_blk start_blk: percent ((current_blk - start_blk) / total_blks) * 100 sys.stdout.write(f\r 进度: {percent:.2f}% | 当前块: {current_blk}) sys.stdout.flush() try: # 定位到指定块 f.seek(current_blk * BLOCK_SIZE) # 读取一个块 data f.read(BLOCK_SIZE) # 如果读取长度不足说明到达设备末尾或出错 if len(data) BLOCK_SIZE: log_message(f\n 警告: 在块 {current_blk} 读取不完整可能已到达设备末尾) break except OSError as e: # 捕获 IO 错误视为坏块 bad_count 1 log_message(f\n [!] 发现坏块 at Block: {current_blk} (Error: {e.strerror})) # 写入结果文件 out_f.write(f{current_blk}\n) out_f.flush() # 确保立即写入磁盘 # 跳跃 500MB log_message(f - 跳跃 {SKIP_MB}MB ({SKIP_BLOCKS} blocks)) current_blk SKIP_BLOCKS continue # 正常读取移动到下一个块 current_blk 1 except PermissionError: log_message(f错误: 权限不足无法访问 {dev_path}。请使用 sudo 运行。) sys.exit(1) except FileNotFoundError: log_message(f错误: 设备 {dev_path} 不存在。) sys.exit(1) sys.stdout.write(\n) # 换行 return bad_count def main(): log_message(f开始执行坏块扫描脚本) log_message(f目标设备: {TARGET_DEV}) log_message(f输入文件: {INPUT_FILE}) log_message(f输出文件: {OUTPUT_FILE}) log_message(f跳跃大小: {SKIP_MB} MB) # 检查 root 权限 if os.geteuid() ! 0: log_message(警告: 建议以 root (sudo) 权限运行以访问原始设备) # 读取分区范围 ranges read_ranges(INPUT_FILE) if not ranges: log_message(未找到有效的扫描范围退出。) return log_message(f共找到 {len(ranges)} 个扫描区间) # 打开输出文件 (追加模式避免覆盖之前的记录如果需要清空可改为 w) # 这里为了每次运行干净先清空如果希望累积则去掉 os.remove # if os.path.exists(OUTPUT_FILE): # os.remove(OUTPUT_FILE) total_bad 0 try: with open(OUTPUT_FILE, a) as out_f: for idx, (start, end) in enumerate(ranges): log_message(f\n--- 扫描区间 {idx1}/{len(ranges)}: 块 {start} 至 {end} ---) # 执行扫描 bads scan_device(TARGET_DEV, start, end, out_f) total_bad bads log_message(f区间 {idx1} 完成发现 {bads} 个坏块) except KeyboardInterrupt: log_message(\n用户中断扫描) log_message(f\n扫描全部结束。总共发现 {total_bad} 个坏块。) log_message(f结果已保存至: {OUTPUT_FILE}) if __name__ __main__: main()最后提醒一下做分区之前一定要设置自定义磁盘备份分区错了也不用怕 dd ifimg of/dev/sdx bs512 count8
返回列表