1. 为什么我弃用了 fdisk,开始认真研究 sfdisk
先讲个实际场景。我之前管理一批 Linux 服务器,磁盘分区这件事平时看着不起眼,真到扩容、重做分区表、批量初始化机器的时候,fdisk 那种交互式操作就会让人抓狂。尤其是一台机器要分 6 块数据盘,每块盘分一个分区,然后格式化挂载,用 fdisk 一块一块敲,手指头都能给你磨出茧子来。更麻烦的是,脚本里没法优雅地处理交互式命令,要么用 echo 管道硬喂,要么装 expect 去模拟键盘输入,怎么看怎么别扭。
后来我认真看了一下 sfdisk 这个工具,才发现它其实一直就在 util-linux 包里,属于系统自带的标配。它的核心思路和 fdisk 完全不同:fdisk 是交互式的,适合人坐在终端前一盘一盘地手动操作;sfdisk 是非交互式的,适合脚本、批处理、自动化场景,可以直接用一行命令把整个分区表“灌”进磁盘。而且它的输入输出格式非常规整,既能导出当前分区表,也能从文件恢复分区表,配合脚本做批量部署简直顺手。
这篇文章我就把 sfdisk 的常用姿势、底层逻辑、以及我实际踩过的坑一次说完。适合刚接触 Linux 分区管理的读者,也适合已经在用 fdisk 但想转向自动化操作的人。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. sfdisk 到底是什么,它和 fdisk、parted 的分工有何不同
2.1 工具定位:一个“脚本友好”的分区工具
sfdisk 是 util-linux 软件包提供的分区工具之一。它和 fdisk 共用同一套分区逻辑代码,读取和写入的分区表格式完全兼容。区别在于使用方式:
- fdisk 面向人工交互,启动后进入一个命令行界面,用 n、d、w 这类单字母命令完成操作。
- sfdisk 面向脚本和批处理,所有操作都通过命令行参数或标准输入传递,不需要进入交互界面。
用一句话概括:fdisk 是“坐在终端前手动操作”,sfdisk 是“把命令写进脚本一键执行”。
2.2 和 parted 的区别
很多人会把 sfdisk 和 parted 搞混。parted 是 GNU 项目维护的分区工具,支持的分区表类型更多,包括 GPT、MBR、以及更冷门的 BSD、Sun 等格式。sfdisk 在较新版本中同样支持 GPT 和 MBR,但在处理一些高级特性时需要额外参数。
以我个人的使用习惯来说:
- 如果需要交互式操作,或者要处理非标准的磁盘标签格式,用 parted。
- 如果要做脚本化批量分区,尤其是在多台机器上执行相同分区方案,用 sfdisk 更直接。
- 如果只是简单查看分区信息、备份分区表,sfdisk 和 parted 都能干,但 sfdisk 的输出更贴近脚本可解析的格式。
2.3 sfdisk 的底层能力
从内核的角度看,sfdisk 做的事其实就是向块设备发送 BLKRRPART 和 BLKPG 之类的 ioctl 请求,让内核重新读取分区表。它本身不负责格式化文件系统,也不负责挂载,它只负责“定义磁盘的分区布局”。
这个定位非常纯粹。所以你在用 sfdisk 时,不需要考虑文件系统类型、挂载点这些后续步骤。它的任务就是把分区表写对,剩下的交给 mkfs、mount 去处理。理解了这一点,你就明白为什么 sfdisk 的命令行设计会那么“干净”。
3. 最常用的 sfdisk 操作,从查看、导出到写入
3.1 查看分区表
最简单也最常用的功能,就是查看设备当前的分区表:
bash复制sfdisk -d /dev/sdb
输出大概长这样:
code复制label: dos
label-id: 0x12345678
device: /dev/sdb
unit: sectors
/dev/sdb1 : start= 2048, size= 2097152, type=83, bootable
/dev/sdb2 : start= 2099200, size= 41943040, type=83
这个输出格式很容易理解。每行一个分区,以字节为单位或扇区为单位,记录了起始位置、大小、类型。这个格式可以直接重定向到文件,作为分区表的备份,也可以在另一台机器上直接用来恢复。
如果想看更详细的信息,可以用 sfdisk -l:
bash复制sfdisk -l /dev/sdb
这个命令输出的内容更接近 fdisk -l 的效果,包括磁盘总容量、扇区大小、分区表类型、各分区起止位置等。
3.2 备份分区表
分区表的备份是个容易被忽视的操作。尤其是生产环境的机器,如果分区表损坏了,而你又没有记录原始布局,恢复起来会比较麻烦。
备份命令很简单:
bash复制sfdisk -d /dev/sdb > sdb-partition-backup.txt
这个文本文件保存了磁盘的标签类型、设备名、以及每个分区的精确起始和大小。建议把每台服务器的分区表备份都存到一个统一目录里,配合主机名或序列号命名。
3.3 从文件恢复分区表
恢复分区表时,直接把备份文件喂给 sfdisk:
bash复制sfdisk /dev/sdb < sdb-partition-backup.txt
sfdisk 会读取文件中的分区定义,清空目标磁盘的分区表,然后写入新的分区表。注意:这个操作会覆盖目标磁盘上的所有分区定义,但不会清除分区里的实际数据。
这里要说一个很多人会误解的点:写入分区表不等于删除数据。分区表只是磁盘上的一个结构,它记录了分区的边界。当你用 sfdisk 重写分区表时,数据依然在扇区里,只是分区的定义变了。所以理论上,只要你知道原始的起始位置和大小,分区表损坏后是可以重新写回去并找回数据的。这也是为什么备份分区表这么重要。
3.4 在脚本中创建新分区
sfdisk 最常见的自动化场景,是在新磁盘上创建分区。例如给 /dev/sdb 创建一个占满整个磁盘的主分区:
bash复制echo 'type=83' | sfdisk /dev/sdb
这条命令的意思是:在 /dev/sdb 上创建一个类型为 83(Linux native)的分区,起始位置默认从扇区 2048 开始,大小默认占满剩余空间。
如果你想一次创建多个分区,可以把多个分区定义写进一个输入文件,或者用 printf 生成多行输入:
bash复制printf 'type=83\n type=83\n' | sfdisk /dev/sdb
这会在 /dev/sdb 上创建两个 Linux 分区。第一个分区默认占磁盘容量的一半左右,第二个分区占剩余空间的一半左右,以此类推。如果你想要精确的大小,就需要显式指定分区的 size 参数。
3.5 构建带有精确大小的分区方案
这是 sfdisk 真正体现“脚本化”优势的地方。假设我要在 /dev/sdc 上创建三个分区:
- sdc1:2GB,类型为 Linux,用途是系统分区
- sdc2:4GB,类型为 Linux
- sdc3:剩余空间,类型为 Linux
输入文件可以这样写:
code复制label: dos
unit: sectors
/dev/sdc1 : start= 2048, size= 4194304, type=83
/dev/sdc2 : start= 4196352, size= 8388608, type=83
/dev/sdc3 : start= 12584960, size= , type=83
注意 sdc3 的 size 留空,sfdisk 会自动把它计算为剩余空间。起始位置必须和上一分区的结束位置衔接,通常起始 = 上一分区起始 + 上一分区大小。在示例中,sdc1 从 2048 开始,大小为 4194304 扇区,所以 sdc2 从 2048 + 4194304 = 4196352 开始。sdc2 大小为 8388608 扇区,所以 sdc3 起始为 4196352 + 8388608 = 12584960。
不过在实际操作中,我不太建议手动计算这些十六进制或十进制的扇区数字,非常容易出错。更稳妥的做法是先用 sfdisk -d 导出一个参考格式,然后基于它修改,或者让 sfdisk 用自动分配的方式,再由 fdisk 或脚本调整。
其实 sfdisk 有一种更简便的写法。在较新版本中,可以直接用逗号分隔的大小描述。比如:
code复制label: dos
/dev/sdc1 : size=+2G, type=83
/dev/sdc2 : size=+4G, type=83
/dev/sdc3 : type=83
含义非常直观:第一个分区比磁盘起始位置多 2GB,第二个分区再分配 4GB,第三个分区占剩余。这种方式比手动算扇区号要友好得多,强烈推荐。
4. 实战:用 sfdisk 完成一个完整的磁盘初始化和挂载流程
4.1 场景描述
假设我收到一台新服务器,有一块空盘 /dev/sdb,需要完成以下操作:
- 创建一个 GPT 分区表。
- 创建三个分区:sdb1 用于系统备份,sdb2 用于数据存储,sdb3 用于日志。
- 三个分区分别格式化为 ext4。
- 挂载到指定目录。
4.2 第一步:确认磁盘状态
先确认 /dev/sdb 确实是空盘,避免误操作:
bash复制lsblk /dev/sdb
如果输出显示该设备没有任何分区,就可以放心操作。如果有分区,先确认这些分区确实不需要保留,再继续。
4.3 第二步:创建 GPT 分区表并写入分区定义
GPT 分区表在 sfdisk 中的标签是 gpt。输入文件如下:
code复制label: gpt
unit: sectors
/dev/sdb1 : size=+20G, type=83
/dev/sdb2 : size=+100G, type=83
/dev/sdb3 : type=83
执行:
bash复制sfdisk /dev/sdb < partition.sfdisk
执行完成后,用 sfdisk -l /dev/sdb 检查一下结果,确认分区大小和类型符合预期。
4.4 第三步:格式化文件系统
分区表写好后,格式化命令就很简单了:
bash复制mkfs.ext4 /dev/sdb1
mkfs.ext4 /dev/sdb2
mkfs.ext4 /dev/sdb3
这一步不是 sfdisk 的职责,但它是完整初始化流程中绕不开的一环。格式化之前再确认一次分区设备名,特别是刚做完分区表变更后,设备名理论上不会变,但养成确认习惯总是好的。
4.5 第四步:创建挂载点并挂载
bash复制sudo mkdir -p /backup /data /logs
sudo mount /dev/sdb1 /backup
sudo mount /dev/sdb2 /data
sudo mount /dev/sdb3 /logs
然后写入 /etc/fstab 实现开机自动挂载。建议使用 UUID 而不是设备名,因为设备名在重启后可能变化。获取 UUID 的方法:
bash复制blkid /dev/sdb1 /dev/sdb2 /dev/sdb3
将输出的 UUID 填入 /etc/fstab,格式如下:
code复制UUID=xxxx-xxxx-xxxx /backup ext4 defaults 0 2
UUID=yyyy-yyyy-yyyy /data ext4 defaults 0 2
UUID=zzzz-zzzz-zzzz /logs ext4 defaults 0 2
4.6 实测中的一个小插曲
我之前在做这一步时,遇到过一个很隐蔽的问题:分区表写入成功了,但 partprobe 或者内核没有立即识别新分区,导致 mkfs 时报找不到设备文件。这个一般是因为分区表没有触发内核重新读取。
解决方法是:
bash复制partprobe /dev/sdb
或者,在部分系统上需要重新加载设备:
bash复制blockdev --rereadpt /dev/sdb
出现这个问题的原因,通常是之前该磁盘上存在被占用的分区或有缓存未刷新。用 sfdisk 写完后,最好确认一下内核是否已经识别到新分区,再继续后续格式化。
5. 从 MBR 到 GPT:sfdisk 在两种格式下的使用差异
5.1 MBR 格式下的限制
MBR 是老旧的磁盘分区标准,使用 32 位逻辑块地址,最大支持 2TB 磁盘(实际上在 512 字节扇区的情况下是 2TiB 左右)。分区数量上,MBR 主分区最多 4 个,如果要超过 4 个,就得把其中一个主分区设为扩展分区,再在扩展分区里划分逻辑分区。sfdisk 支持在 MBR 下完整定义这些类型,包括逻辑分区:
code复制label: dos
/dev/sdb1 : size=+10G, type=83
/dev/sdb2 : size=+10G, type=83
/dev/sdb3 : size=+10G, type=83
/dev/sdb4 : size=+10G, type=83
/dev/sdb5 : size=+10G, type=83
在这个例子中,sfdisk 会自动将 sdb1 到 sdb4 作为主分区,sdb5 作为扩展分区中的逻辑分区。这种自动处理让 MBR 下的多分区创建简单了不少。
5.2 GPT 格式下的优势
GPT 是现代磁盘的标准格式,支持最多 128 个分区(默认情况下),支持 2TB 以上的磁盘,并且自带冗余分区表头和 CRC 校验,比 MBR 更加健壮。
使用 sfdisk 创建 GPT 分区表时,只需要在输入文件中指定 label: gpt。对于支持 UEFI 的机器,通常需要创建一个类型为 EFI System Partition 的分区。在 sfdisk 中,设置类型可以用 type=uefi:
code复制label: gpt
/dev/sdb1 : size=+512M, type=uefi
/dev/sdb2 : size=+32G, type=83
/dev/sdb3 : type=83
5.3 转换格式时的注意事项
当你想把一块磁盘从 MBR 转换为 GPT(或反过来),不能简单地在现有分区表上直接修改。最稳妥的方式是清空分区表再重写。sfdisk 提供了一个名为 --wipe 的选项,可以在写入新分区表前清除旧的分区签名、文件系统签名等:
bash复制sfdisk --wipe always /dev/sdb < partition.gpt.sfdisk
不过说实话,如果磁盘上有重要数据,我不会在运行中的系统上直接做这种转换。我会先把数据备份出去,然后离线处理磁盘,或者直接换一块新盘。这种转换操作的风险和收益在大部分场景下不成比例。
6. 排查链路:sfdisk 操作后分区不生效,我一般这样查
6.1 问题现象
刚用 sfdisk 写入分区表后,lsblk 看不到新分区,或者 fdisk -l 能看到但 mkfs 报错找不到设备。
6.2 排查第 1 步:确认分区表是否真的写入了
先看 sfdisk -l 的输出,确认分区表本身是否正常。如果输出里能看到分区,说明分区表结构没问题,问题可能出在内核识别环节。
6.3 排查第 2 步:触发内核重读
执行:
bash复制partprobe /dev/sdb
或者:
bash复制blockdev --rereadpt /dev/sdb
然后再次查看 lsblk。大部分情况下,这一步就能解决。
如果仍然看不到,检查系统日志:
bash复制dmesg | tail -20
内核通常会输出 "unable to open /dev/sdb1" 或类似的信息,说明设备节点没有创建。这种情况下,可以尝试手动创建设备节点,但更实际的做法是重启。
6.4 排查第 3 步:确认没有进程占用分区
如果磁盘上有某个分区处于挂载状态,内核可能拒绝重新读取分区表。用 lsof 或 fuser 检查占用,先卸载再重试。
6.5 排查第 4 步:是否使用了过旧的 util-linux 版本
新版 sfdisk 在 GPT 支持方面比较完善,但老版本可能存在缺陷。如果排查完前三步仍然有问题,注意一下 util-linux 版本:
bash复制sfdisk --version
如果版本过旧,建议升级到较新版本。企业环境的机器有时候系统版本比较老,包管理器里的 util-linux 可能长期没有更新,这时候需要走厂商的更新渠道。不过这种情况在主流发行版上不多见,大部分时候前两步就能解决问题。
7. 我踩过的那些坑,以及一些实用小技巧
7.1 坑 1:用 sfdisk -d 备份后,再恢复时设备名对不上
sfdisk -d 输出的文件里包含 device: /dev/sdb 这一行。如果原设备名是 sdb,但恢复目标变成了 sdc,sfdisk 会怎么处理?
实测结果是,sfdisk 在恢复时会忽略输入中的设备名,只使用命令行参数指定的目标设备。所以不用担心因为设备名变化导致恢复失败。但要注意,输入文件里的分区定义如果带有设备前缀,也会被忽略。这个设计对脚本编写者很友好。
7.2 坑 2:分区表写入后,数据看起来“没消失”,但分区消失
这种场景我遇到过不止一次。某个磁盘原本有分区,我误执行了 sfdisk 写入了一个只有两个分区的新定义,然后旧分区就再也看不到了,但数据实际上还在磁盘上。
恢复方法是:找到原始的 sfdisk -d 备份,重新恢复。或者,如果你知道原始分区的起始和大小,手动重建分区表也能找回数据。但前提是你必须确切知道这些参数。这个例子再次印证了备份分区表的价值。
7.3 坑 3:脚本中运行 sfdisk 时没有提升权限
sfdisk 修改分区表需要 root 权限,普通用户执行会直接报错。在脚本里务必用 sudo 或让脚本以 root 身份运行。别小看这个,我见过有同事在自动化脚本里忘了加 sudo,导致批量部署中断,排查了半天才发现是权限问题。
7.4 小技巧 1:使用 --json 输出,方便程序解析
新版本的 sfdisk 支持 JSON 格式输出:
bash复制sfdisk --json /dev/sdb
输出包含设备路径、扇区大小、分区列表等结构化数据,非常适合用 Python 或其他语言做进一步处理。如果你在写自动化脚本,需要解析分区信息,这个功能相当实用。
7.5 小技巧 2:用 --show-size 查看磁盘大小
bash复制sfdisk --show-size /dev/sdb
默认输出以字节为单位,也可以加上 --unit=sectors 让输出以扇区为单位。这个命令适合在脚本中获取磁盘容量,避免自己解析 /proc/partitions。
7.6 小技巧 3:指定分区的起始位置
有时候你需要在特定位置创建分区,比如为引导加载器预留空间。显式指定 start:
code复制label: dos
/dev/sdb1 : start=2048, size=+1G, type=83
从第 2048 号扇区开始,这是现代 Linux 分区的常见起始位置,为引导加载器或元数据预留了 1MiB 空间。
7.7 小技巧 4:批量初始化多块磁盘
写一个简单的 shell 循环,可以让多块磁盘用同一个分区方案初始化:
bash复制for disk in sdb sdc sdd sde; do
sfdisk /dev/$disk < /etc/partition-template.sfdisk
mkfs.ext4 /dev/${disk}1
done
这种方式在测试环境搭建、K8s 节点初始化、数据节点批量上线等场景下非常实用。唯一要提醒的是,批量操作前务必确认磁盘设备名列表准确无误,否则可能把系统盘也一起格式化。
8. 从分区表到文件系统:sfdisk 后续的常用配套命令
分区表写完之后,整个磁盘初始化的后半段还需要其他工具配合。这里顺便梳理一下我常用的命令链。
8.1 格式化:mkfs
分区创建后,必须格式化文件系统才能挂载使用。常见格式有 ext4、xfs、btrfs。选择哪种取决于业务场景:
- 追求稳定和兼容性,选 ext4。
- 追求大文件和高吞吐,选 xfs。
- 需要子卷、快照等特性,选 btrfs,但要接受它相对更复杂的运维。
8.2 获取 UUID:blkid
写入 /etc/fstab 时,建议用 UUID 而不是设备名。blkid 命令可以输出分区的 UUID 和文件系统类型。在脚本中可以这样提取:
bash复制uuid=$(blkid -s UUID -o value /dev/sdb1)
echo $uuid
注意,这个操作要求指定分区存在且已经格式化。如果还没格式化,blkid 输出为空,别急着在 fstab 里填内容。
8.3 验证挂载配置:mount -a
修改完 /etc/fstab 后,先不要重启,用 mount -a 验证一下所有条目是否正确。如果有错误,mount 会直接报错,这时候可以立即修复,避免重启后系统起不来。
8.4 查看磁盘使用情况:df -h
挂载完成后,用 df -h 检查一下容量、使用率、挂载点,确保一切正常。这一步虽然简单,但能确认前面的整条链路都走通了。
9. 写在最后,关于 sfdisk 的几个使用习惯建议
用 sfdisk 已经挺长时间了,从最初的“fdisk 能干嘛要换”到后来“批量分区离不了它”,感受比较深的是:工具没有高下之分,关键是场景匹配。fdisk 适合交互式维护单台机器,sfdisk 天生就是为脚本和自动化准备的。如果你的工作场景经常涉及多台机器的磁盘初始化、分区表备份恢复,那 sfdisk 值得投入时间好好掌握。
在具体使用中,我最推荐的做法是:每台重要服务器初始完成分区后,立刻用 sfdisk -d 导出一份分区表备份,和系统初始化记录放在一起。这个动作花不了几秒钟,但关键时刻能救命。
另外,尽量用新版 util-linux。新版 sfdisk 在很多细节上做了优化,比如更完善的分区类型别名、更友好的错误信息、更好的 GPT 兼容性。如果你的系统版本过老,建议通过官方仓库或合规方式升级 util-linux,别一直停留在旧版本上。
最后分享一个小技巧:你可以把常用分区方案做成模板文件,放在 /etc 下的一个固定目录里,比如 /etc/sfdisk-templates/,这样批量部署时只需复制模板、替换设备名,直接喂给 sfdisk 就行。我目前管理的大多数数据节点,分区方案都是这样一套模板打天下,省去了大量重复操作,也降低了手工敲键盘出错的概率。
