效果不错
最后提醒一下,做分区之前一定要设置自定义磁盘,备份分区,错了也不用怕 dd if=img of=/dev/sdx bs=512 count=8
备份分区的原理就是前面4K,一个扇区512,1K是2个扇区,4k是8个扇区,也就是常说的4k,
linux 格式化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.py
sudo 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.py
update_log
V0.11-260830第二个版本
新增备份全部分区的功能,防止错误选择磁盘导致问题
sudo python3 3.1-v0.10_auto_backup_pt.py
#百度关键提示词词生成代码
#python 用linux的dd命令导出 所有磁盘 分区表 根据日期时间和磁盘名称 到文件
#能否提供一个更简洁的版本
改成日期加时间的格式 保存在日期加格式的文件夹里面 把备份大小改成4096
改成日期加时间的格式 保存在日期加格式精确到秒的文件夹里面 把备份大小改成4096
3.1-v0.10_auto_backup_pt.py
#自述,本人只是想到的结合百度ai生成的缝合怪,专业的高手不要笑话就好。很多结构和逻辑经不起推敲,只是缝合一起按流程走通了
#有能力整合的希望联系我一起优化一下这缝合怪
#陆陆续续硬盘多多少少坏道就出现了,但是还能分区格式化,
#就突发奇想,怎么利用起来,临时存储也可以,例如下载临时文件,存放游戏客户端。
#为了这点空间如果手动处理肯定不划算还不如买新的,所以出现了缝合怪版本,几个步骤就自动解决隔离坏道坏块问题
下载地址
https://download.csdn.net/download/cyuyan112233/93366615
不知道你们能下载么上面的,下面我贴上代码吧,不用费劲了
sudo python3 1-v0.12-chk_disk_skip_500M.py
import 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(f"os.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(f"ioctl 尝试失败: {e}") finally: os.close(fd) except Exception as e: print(f"打开设备文件失败: {e}") # 方法 3: 使用 blockdev 命令作为最后的手段 try: result = subprocess.run( ['blockdev', '--getsize64', device_path], stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True, timeout=5 ) if result.returncode == 0: return int(result.stdout.strip()) except Exception as e: print(f"blockdev 命令执行失败: {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(f"1. 运行 'lsblk {DEVICE_PATH}' 确认设备存在且显示大小。") print(f"2. 运行 '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_path=INPUT_FILE output_path=INPUT_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.py
import 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_output=True, text=True, check=True) 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_dir="backups"): """ 备份单个磁盘的分区表 (前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_ok=True) 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 命令: # bs=4096: 块大小 4096 字节 # count=1: 只拷贝 1 个块 (总共 4096 字节) # status=none: 静默执行,不输出进度 subprocess.run(['dd', f'if={src}', f'of={dst}', 'bs=4096', 'count=1', 'status=none'], check=True, capture_output=True) # 验证文件大小 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_blk=256 * (512 + 50) ALL_end_blk_xor_blk=256 * 50 def check_root(): if os.geteuid() != 0: print("错误: 此脚本需要 root 权限 (sudo) 才能操作原始磁盘设备。") sys.exit(1) def check_dependencies(): if not subprocess.run(['which', SGDISK_CMD], stdout=subprocess.PIPE, stderr=subprocess.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[i+1]) - 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[i+1]}'。{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], stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True ) if result.returncode != 0: # 有时如果磁盘全是0,sgdisk可能会报错说没发现有效签名,这通常是可以接受的 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], stdout=subprocess.DEVNULL, stderr=subprocess.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, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True ) 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], stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True ) 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_blk=256 * (512 + 50) ALL_end_blk_xor_blk=256 * 50 def check_root(): if os.geteuid() != 0: print("错误: 此脚本需要 root 权限 (sudo) 才能操作原始磁盘设备。") sys.exit(1) def check_dependencies(): if not subprocess.run(['which', SGDISK_CMD], stdout=subprocess.PIPE, stderr=subprocess.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[i+1]) - 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[i+1]}'。{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], stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True ) if result.returncode != 0: # 有时如果磁盘全是0,sgdisk可能会报错说没发现有效签名,这通常是可以接受的 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], stdout=subprocess.DEVNULL, stderr=subprocess.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, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True ) 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], stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True ) 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.py
import 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[i+1]) if start < end: ranges.append((start, end)) else: log_message(f"跳过无效范围: {start} - {end}") except ValueError: log_message(f"跳过非数字行: {lines[i]}, {lines[i+1]}") 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--- 扫描区间 {idx+1}/{len(ranges)}: 块 {start} 至 {end} ---") # 执行扫描 bads = scan_device(TARGET_DEV, start, end, out_f) total_bad += bads log_message(f"区间 {idx+1} 完成,发现 {bads} 个坏块") except KeyboardInterrupt: log_message("\n用户中断扫描") log_message(f"\n扫描全部结束。总共发现 {total_bad} 个坏块。") log_message(f"结果已保存至: {OUTPUT_FILE}") if __name__ == "__main__": main()最后提醒一下,做分区之前一定要设置自定义磁盘,备份分区,错了也不用怕 dd if=img of=/dev/sdx bs=512 count=8