如何在不提升整个Python脚本权限的情况下获取root权限以查询物理磁盘及其容量
嘿,我太懂你这个需求了——谁都不想把整个Python脚本都跑在root权限下,毕竟权限越大,万一出点小错风险就越高。刚好我之前也碰到过类似的问题,给你分享几个实用的解决思路,挑适合你的来用:
思路1:把权限敏感的代码拆成独立小脚本,用sudo按需调用
这是最常用也最安全的方法:把需要root权限的磁盘查询逻辑单独抽成一个小脚本,主脚本保持普通权限,只在需要查磁盘的时候,通过sudo调用这个小脚本。
主脚本(普通权限运行)
主脚本负责你的核心业务逻辑,只有查询磁盘的时候才触发提权:
import subprocess import json def fetch_disk_details(): try: # 调用提权的磁盘查询助手脚本 proc = subprocess.run( ["sudo", "python3", "/绝对路径/到/disk_query_helper.py"], capture_output=True, text=True, check=True ) # 把助手脚本返回的JSON转成字典用 return json.loads(proc.stdout) except subprocess.CalledProcessError as e: print(f"查询磁盘出错:{e.stderr}") return {} # 你的正常业务逻辑 if __name__ == "__main__": disk_info = fetch_disk_details() for disk, capacity in disk_info.items(): print(f"磁盘 {disk}:{capacity}")
磁盘查询助手脚本(仅通过sudo调用)
这个脚本只做一件事:用pyparted查物理磁盘,然后把结果转成JSON输出给主脚本:
import parted import json def query_physical_disks(): disks = {} # 遍历所有块设备,过滤掉分区、loop设备、逻辑卷 for device in parted.getAllDevices(): is_physical_disk = ( not device.path.startswith("/dev/loop") and not device.path.startswith("/dev/mapper") and not device.path.startswith("/dev/dm-") ) if is_physical_disk: # 把字节转成易读的GB格式 capacity_gb = device.getSize() / (1024 ** 3) disks[device.path] = f"{capacity_gb:.2f} GB" return disks if __name__ == "__main__": # 输出JSON格式,方便主脚本解析 print(json.dumps(query_physical_disks()))
配置免密sudo(可选但实用)
如果不想每次调用都输密码,可以编辑sudoers文件(必须用visudo命令,不能直接编辑!),加一行:
你的用户名 ALL=(ALL) NOPASSWD: /usr/bin/python3 /绝对路径/到/disk_query_helper.py
加完后可以用sudo visudo -c检查语法,避免出错锁死sudo权限。
思路2:直接读系统sysfs文件,完全不需要root
其实Linux系统把很多硬件信息存在/sys/class/block/目录下,普通用户默认就有读权限!我们可以直接读这些文件,根本不需要root权限,这是最省心的方法:
import os def get_disk_info_from_sysfs(): disk_details = {} block_root = "/sys/class/block" for disk_name in os.listdir(block_root): disk_path = os.path.join(block_root, disk_name) # 判断是不是物理磁盘:有device目录,且不是loop/逻辑卷设备 is_physical = ( os.path.exists(os.path.join(disk_path, "device")) and not disk_name.startswith("loop") and not disk_name.startswith("dm-") ) if not is_physical: continue # 读扇区数,每个扇区默认512字节 size_file = os.path.join(disk_path, "size") with open(size_file, "r") as f: sector_count = int(f.read().strip()) # 转成GB格式 capacity_gb = (sector_count * 512) / (1024 ** 3) disk_details[f"/dev/{disk_name}"] = f"{capacity_gb:.2f} GB" return disk_details # 直接普通权限运行就行 if __name__ == "__main__": disks = get_disk_info_from_sysfs() for disk, cap in disks.items(): print(f"{disk}: {cap}")
这个方法的好处是完全不需要任何提权操作,而且性能还快,唯一要注意的是不同发行版的sysfs结构可能略有差异,但大部分桌面/服务器Linux(Ubuntu、CentOS、Debian这些)都通用。
思路3:用C wrapper设置setuid(谨慎使用)
如果必须用pyparted又不想用sudo,可以写一个简单的C程序当wrapper,编译后设置setuid root,这样普通用户运行这个wrapper就能以root权限执行Python查询脚本。不过这个方法有安全风险,一定要确保脚本路径是绝对路径且脚本本身不能被普通用户修改。
C wrapper代码
#include <stdio.h> #include <stdlib.h> #include <unistd.h> int main() { // 切换到root权限 if (setuid(0) != 0) { perror("切换root权限失败"); return 1; } // 调用磁盘查询脚本,注意填绝对路径 execl("/usr/bin/python3", "python3", "/绝对路径/到/disk_query_helper.py", NULL); perror("执行脚本失败"); return 1; }
编译和设置权限
# 编译成可执行文件 gcc disk_wrapper.c -o disk_wrapper # 把所有权改成root sudo chown root:root disk_wrapper # 设置setuid位,让普通用户运行时继承root权限 sudo chmod u+s disk_wrapper
之后普通用户直接运行./disk_wrapper就能得到磁盘信息了,不过一定要把disk_query_helper.py的权限设成只有root能修改,避免被篡改。
最后给你几个小提醒
- 优先用思路2,完全不需要提权,安全又省心;
- 如果必须用pyparted,选思路1,权限隔离得最好;
- 思路3尽量少用,除非万不得已,毕竟setuid程序的安全风险更高;
- 不管用哪种方法,一定要过滤掉loop设备、逻辑卷这些非物理磁盘的设备,避免拿到错误数据。
这样应该就解决你的问题了,有啥细节不清楚的随时问哈!




