Python读取硬盘所有信息的完整方法

在 Python 中读取硬盘的所有信息是一个复杂的任务,因为它涉及到操作系统底层接口、硬件抽象和管理员权限。没有一个单一的库可以跨平台地获取所有信息。

图片[1]_Python读取硬盘所有信息的完整方法_知途无界

下面我将为您提供一个分层次、跨平台的完整方法,涵盖从基础的文件系统信息到深入的磁盘硬件信息。

核心思路

  1. 分层获取​:先获取高级的逻辑分区信息(如 Windows 的 C: 盘,Linux 的 /dev/sda1),再深入到物理磁盘信息(如 /dev/sda),最后尝试获取最底层的 SMART 健康数据。
  2. 跨平台工具​:
    • Windows: 主要依赖 wmipywin32 库来调用 WMI (Windows Management Instrumentation)。
    • Linux: 主要依赖解析 /sys/class/block, /proc/mounts 和调用系统命令(如 lsblk, fdisk, smartctl)。
    • macOS: 主要依赖 subprocess 模块调用系统命令(如 diskutil)。

准备工作:安装必要的库

首先,您需要安装一些第三方库。

# 跨平台的 WMI 访问(主要用于 Windows)
pip install wmi

# 如果需要更深入的 Windows API 访问
pip install pywin32

# 用于执行系统命令(Linux/macOS 必需)
# 标准库,无需安装

# 注意:要获取 SMART 信息,必须在系统上安装 smartmontools
# Linux (Debian/Ubuntu): sudo apt-get install smartmontools
# macOS: brew install smartmontools
# Windows: 下载并安装 smartmontools for Windows

完整代码实现

以下脚本提供了一个尽可能完整的示例,您可以根据目标平台运行相应的部分。

import os
import sys
import platform
import subprocess
import json
from collections import defaultdict

# ==================== Windows 特定功能 ====================
def get_windows_drive_info():
    """使用 WMI 获取 Windows 驱动器信息"""
    try:
        import wmi
        c = wmi.WMI()
        drive_info = []

        # 获取逻辑磁盘(分区)信息
        for disk in c.Win32_LogicalDisk():
            info = {
                "DeviceID": disk.DeviceID,  # e.g., "C:"
                "DriveType": {2: "Removable", 3: "Local Disk", 5: "CD-ROM"}.get(disk.DriveType, "Unknown"),
                "FileSystem": disk.FileSystem,
                "FreeSpaceGB": round(int(disk.FreeSpace) / (1024**3), 2),
                "SizeGB": round(int(disk.Size) / (1024**3), 2) if disk.Size else 0,
                "VolumeName": disk.VolumeName,
            }
            drive_info.append(info)

        # 获取物理磁盘信息
        physical_disks = []
        for disk in c.Win32_DiskDrive():
            # 关联分区
            partitions = []
            for partition in disk.associators("Win32_DiskDriveToDiskPartition"):
                logical_disks = [ld.DeviceID for ld in partition.associators("Win32_LogicalDiskToPartition")]
                partitions.append({
                    "Name": partition.Name,
                    "Description": partition.Description,
                    "LogicalDisks": logical_disks
                })

            disk_data = {
                "Model": disk.Model,
                "InterfaceType": disk.InterfaceType,
                "MediaType": disk.MediaType,
                "SerialNumber": disk.SerialNumber.strip() if disk.SerialNumber else "N/A",
                "SizeGB": round(int(disk.Size) / (1024**3), 2) if disk.Size else 0,
                "Partitions": partitions
            }
            physical_disks.append(disk_data)

        return {"logical_drives": drive_info, "physical_disks": physical_disks}

    except ImportError:
        print("请安装 wmi 库: pip install wmi")
        return None
    except Exception as e:
        print(f"WMI 查询失败: {e}")
        return None


def get_smart_info_windows(device_id):
    """
    在 Windows 上使用 smartctl 获取 SMART 信息
    device_id 格式如 "\\\\.\\PhysicalDrive0"
    """
    try:
        # 注意:需要管理员权限运行 Python
        cmd = f'smartctl -a "{device_id}"'
        result = subprocess.run(cmd, shell=True, capture_output=True, text=True, check=True)
        # 简单解析输出,实际应用中可能需要更复杂的解析
        return result.stdout
    except subprocess.CalledProcessError as e:
        return f"无法获取 SMART 信息: {e.stderr}"
    except FileNotFoundError:
        return "未找到 smartctl,请确保已安装 smartmontools 并将其添加到 PATH。"


# ==================== Linux 特定功能 ====================
def get_linux_drive_info():
    """在 Linux 上通过解析文件和命令获取驱动器信息"""
    drive_info = {"logical_mounts": [], "physical_disks": []}

    # 1. 获取挂载点信息 (/proc/mounts)
    try:
        with open('/proc/mounts', 'r') as f:
            for line in f:
                parts = line.split()
                if parts[0].startswith('/dev/'):
                    device, mount_point, fs_type = parts[0], parts[1], parts[2]
                    # 避免重复添加
                    if not any(m['MountPoint'] == mount_point for m in drive_info["logical_mounts"]):
                         # 尝试获取磁盘空间信息
                        statvfs = os.statvfs(mount_point)
                        free_gb = round(statvfs.f_frsize * statvfs.f_bavail / (1024**3), 2)
                        total_gb = round(statvfs.f_frsize * statvfs.f_blocks / (1024**3), 2)
                        drive_info["logical_mounts"].append({
                            "Device": device,
                            "MountPoint": mount_point,
                            "FileSystem": fs_type,
                            "FreeSpaceGB": free_gb,
                            "TotalSizeGB": total_gb
                        })
    except Exception as e:
        print(f"读取 /proc/mounts 失败: {e}")

    # 2. 获取物理磁盘信息 (/sys/class/block 和 lsblk)
    try:
        # 列出所有块设备
        block_devices = [d for d in os.listdir('/sys/class/block') if d.startswith(('sd', 'hd', 'nvme'))]
        
        # 使用 lsblk 获取更友好的树状结构
        lsblk_result = subprocess.run(['lsblk', '-o', 'NAME,SIZE,TYPE,MOUNTPOINT,MODEL'], capture_output=True, text=True)
        print("--- lsblk 输出 ---")
        print(lsblk_result.stdout)

        # 手动解析 /sys 来获取型号和序列号(更复杂)
        for dev in block_devices:
            dev_path = f"/sys/class/block/{dev}"
            model_path = os.path.join(dev_path, "device/model")
            vendor_path = os.path.join(dev_path, "device/vendor")
            
            model = "N/A"
            if os.path.exists(model_path):
                with open(model_path, 'r') as f:
                    model = f.read().strip()

            size_path = os.path.join(dev_path, "size")
            size_sectors = 0
            if os.path.exists(size_path):
                with open(size_path, 'r') as f:
                    size_sectors = int(f.read().strip())
            # 扇区大小通常为512字节
            size_gb = round((size_sectors * 512) / (1024**3), 2)

            drive_info["physical_disks"].append({
                "Name": dev, # e.g., sda
                "Model": model,
                "SizeGB": size_gb,
                "FullPath": f"/dev/{dev}"
            })

    except Exception as e:
        print(f"解析 Linux 磁盘信息失败: {e}")

    return drive_info


def get_smart_info_linux(device_path):
    """
    在 Linux 上使用 smartctl 获取 SMART 信息
    device_path 格式如 "/dev/sda"
    """
    try:
        # 注意:需要 root 权限运行
        cmd = f'sudo smartctl -a "{device_path}"'
        result = subprocess.run(cmd, shell=True, capture_output=True, text=True, check=True)
        return result.stdout
    except subprocess.CalledProcessError as e:
        return f"无法获取 SMART 信息 (可能需要 sudo): {e.stderr}"
    except FileNotFoundError:
        return "未找到 smartctl,请确保已安装 smartmontools。"


# ==================== macOS 特定功能 ====================
def get_macos_drive_info():
    """在 macOS 上使用 diskutil 获取驱动器信息"""
    drive_info = {"logical_volumes": [], "physical_disks": []}
    try:
        # 获取卷信息
        result = subprocess.run(['diskutil', 'list', '-plist'], capture_output=True, text=True)
        # plist 解析比较复杂,这里用非 plist 方式演示
        result_json = subprocess.run(['diskutil', 'list', '-json'], capture_output=True, text=True, check=True)
        data = json.loads(result_json.stdout)
        
        # 解析 JSON 输出 (简化版)
        for disk in data.get('AllDisksAndPartitions', []):
            disk_info = {
                "DeviceIdentifier": disk['DeviceIdentifier'], # e.g., disk0
                "Size": disk['Size'],
                "Partitions": []
            }
            for part in disk.get('Partitions', []):
                part_info = {
                    "DeviceIdentifier": part['DeviceIdentifier'], # e.g., disk0s1
                    "MountPoint": part.get('MountPoint'),
                    "Size": part['Size']
                }
                disk_info["Partitions"].append(part_info)
                
                if part.get('MountPoint'):
                     # 获取文件系统信息
                    fs_result = subprocess.run(['df', '-h', part['MountPoint']], capture_output=True, text=True)
                    drive_info["logical_volumes"].append({
                        "Device": part['DeviceIdentifier'],
                        "MountPoint": part['MountPoint'],
                        "FilesystemInfo": fs_result.stdout.splitlines()[1] if len(fs_result.stdout.splitlines()) > 1 else "N/A"
                    })
            
            drive_info["physical_disks"].append(disk_info)

        print(json.dumps(data, indent=2)) # 打印原始 JSON 供参考

    except subprocess.CalledProcessError as e:
        print(f"diskutil 命令失败: {e}")
    except json.JSONDecodeError:
        print("解析 diskutil JSON 输出失败。")
    except Exception as e:
        print(f"获取 macOS 磁盘信息时发生未知错误: {e}")

    return drive_info


def get_smart_info_macos(device_path):
    """
    在 macOS 上使用 smartctl 获取 SMART 信息
    device_path 格式如 "/dev/disk0"
    """
    # 在 macOS 上,设备名通常是 /dev/diskX,但 smartctl 可能需要 /dev/rdiskX
    # 这里直接使用传入的路径
    try:
        cmd = f'smartctl -a "{device_path}"'
        result = subprocess.run(cmd, shell=True, capture_output=True, text=True, check=True)
        return result.stdout
    except subprocess.CalledProcessError as e:
        return f"无法获取 SMART 信息: {e.stderr}"
    except FileNotFoundError:
        return "未找到 smartctl,请确保已安装 smartmontools。"


# ==================== 主函数 ====================
def main():
    system = platform.system()
    print(f"正在检测 {system} 系统的硬盘信息...\n")

    all_disk_data = {}

    if system == "Windows":
        all_disk_data = get_windows_drive_info()
        if all_disk_data and all_disk_data.get("physical_disks"):
            print("\n--- 物理磁盘 SMART 信息 (示例: 第一块磁盘) ---")
            first_disk = all_disk_data["physical_disks"][0]
            device_id = f"\\\\.\\{first_disk['Model'].replace(' ', '_')}" # 构造一个可能的 ID,实际可能需要调整
            # 更可靠的方法是使用 Win32_DiskDrive 的 Index 属性构造 "\\\\.\\PhysicalDrive0"
            # 这里我们简化演示,实际需要映射关系
            print("注意:Windows SMART 查询需要精确的设备标识符,且需管理员权限。")
            # print(get_smart_info_windows("\\\\.\\PhysicalDrive0"))

    elif system == "Linux":
        all_disk_data = get_linux_drive_info()
        if all_disk_data and all_disk_data.get("physical_disks"):
            print("\n--- 物理磁盘 SMART 信息 (示例: 第一块磁盘) ---")
            first_disk = all_disk_data["physical_disks"][0]
            print(get_smart_info_linux(first_disk['FullPath']))

    elif system == "Darwin": # macOS
        all_disk_data = get_macos_drive_info()
        # macOS 的 diskutil 输出已经很丰富,SMART 信息通常也包含在内
        print("\n--- macOS 磁盘信息已通过 diskutil 获取 ---")

    else:
        print(f"不支持的操作系统: {system}")

    # 打印收集到的信息摘要
    print("\n\n========== 收集到的信息摘要 ==========")
    print(json.dumps(all_disk_data, indent=2, ensure_ascii=False))


if __name__ == "__main__":
    # 检查管理员/root权限
    if os.name == 'nt': # Windows
        import ctypes
        if not ctypes.windll.shell32.IsUserAnAdmin():
            print("警告:建议以管理员身份运行以获取完整的 SMART 信息。")
    else: # Linux/macOS
        if os.geteuid() != 0:
            print("警告:建议以 root 权限运行(sudo)以获取完整的 SMART 信息和所有磁盘详情。")

    main()

关键注意事项和局限性

  1. 权限!权限!权限!​
    • Windows: 读取 SMART 信息和某些详细的磁盘属性需要以管理员身份运行​ Python 脚本。
    • Linux/macOS: 读取 /sys/class/block 下的某些文件和执行 smartctl 通常需要 ​root 权限​(使用 sudo)。
  2. SMART 工具的依赖性: 获取硬盘健康状态的核心是 smartmontools。必须在系统上预先安装它,并且 Python 脚本能够找到它(PATH 环境变量)。
  3. 跨平台差异:
    • Windows​ 的 WMI 非常强大,能提供结构化的信息,但仅限于 Windows。
    • Linux​ 的信息分散在 /proc/sys 和各种命令行工具中,需要组合和解析。
    • macOS​ 的 diskutil 是首选工具,但其输出格式(尤其是 plist/json)可能随版本变化。
  4. 信息解析的复杂性: 上述代码中的 SMART 信息解析非常简单(直接返回文本)。在实际应用中,您需要使用正则表达式或专门的解析库(如 pysmart)来提取温度、通电时间、坏道计数等关键属性。
  5. 设备名称映射: 在 Windows 上,将逻辑驱动器(C:)映射到物理磁盘(\\.\PhysicalDrive0)需要知道磁盘分区布局,这在代码中处理起来比较复杂。上面的示例只是演示了基本方法。

总结

这个完整的方案为您提供了一个坚实的基础。您可以根据具体需求,针对您的目标平台,深入开发和优化其中的某个部分。例如,您可以专注于完善 Linux 下的 SMART 信息解析,或者为 Windows 开发一个更健壮的逻辑-物理磁盘映射器。

© 版权声明
THE END
喜欢就点个赞,支持一下吧!
点赞72 分享
评论 抢沙发
头像
欢迎您留下评论!
提交
头像

昵称

取消
昵称表情代码图片

    暂无评论内容