半亩方塘
每次开机都是新主题:GRUB 主题自动轮换指南

每次开机都是新主题:GRUB 主题自动轮换指南

414 次阅读

对双系统用户来说,开机第一眼看到的往往不是桌面,而是 GRUB 的启动菜单。默认的黑底白字界面实在朴素,直到有一天我刷到崩铁角色主题的 GRUB 壁纸,突然意识到:开机这个每天都要经历的小仪式,明明可以更有感觉。

现在的效果是:每次开机,GRUB 都会从已安装的崩铁角色主题里换一个出场——今天是流萤,明天可能就是昔涟。每次开机都有一点新鲜感。这篇文章把我的整套方案写下来,从手动切主题讲起,到双系统自动轮换,全程可直接复制。

GRUB 主题怎么切换?从零开始

主题文件放在哪

GRUB 主题是一个目录,核心文件是 theme.txt(描述布局、字体、颜色),配套若干背景图。约定俗成的存放位置是:

/usr/share/grub/themes/

每个主题一个子目录,比如:

/usr/share/grub/themes/TheHerta_cn/
├── theme.txt
├── background.png
└── ...

手动设置主题

最简单的方式是改 /etc/default/grub,加一行:

sudo vim /etc/default/grub
GRUB_THEME="/usr/share/grub/themes/TheHerta_cn/theme.txt"

然后重新生成引导配置(这步别忘,改了配置不重生成等于白改):

sudo grub-mkconfig -o /boot/grub/grub.cfg

重启即可看到新主题。

顺带推荐:StarRailGrubThemes

崩铁角色主题可以看看这个仓库:StarRailGrubThemes

https://github.com/voidlhf/StarRailGrubThemes

角色主题很全,每个都有 _cn 后缀的中文版(菜单、字体渲染面向中文界面);作者公开了 Figma 设计文件,素材取自 StarRailRes 官方资源站,质量有保障。克隆或下载解压后,把主题目录拷进 /usr/share/grub/themes/,再按上文的方式在 GRUB_THEME 里指一下(或者直接参与后面的自动轮换)就能用。仓库 preview/ 目录下有全部主题的预览图,装之前可以先挑挑看。

让主题自动切换:GRUB 原生轮换方案

手动切主题只是开胃菜。我的核心需求是:每次开机都换一个主题,保持新鲜感。方案我迭代过一次,先说说最终版。

为什么不用 systemd 服务

一开始也想过用 systemd 开机后随机换主题。但在双系统环境下这条路直接走不通:systemd 服务只在我进 Arch 时才运行,开机选 Windows 的话它压根不会执行——主题自然也不会换。而且就算在 Arch 里,主题切换也发生在系统启动之后,你第一眼看到的 GRUB 菜单还是旧的。

所以方案必须是:在引导阶段、进入任何系统之前,由 GRUB 自己完成轮换——这才能保证无论进 Arch 还是 Windows,每次开机菜单都是新主题。

完整实现:/etc/grub.d/01_theme_rotate

很多人不知道,/boot/grub/grub.cfg 是 GRUB 每次开机自行解析执行的脚本,而 grub-mkconfig 只是把它"生成"出来——它会按数字前缀顺序执行 /etc/grub.d/ 下的所有可执行脚本,把它们的输出拼进 grub.cfg。利用这个机制,我们放一个自己的脚本进去,让它在生成 grub.cfg 时枚举主题、输出轮换分支。

创建脚本(root 权限):

sudo vim /etc/grub.d/01_theme_rotate
#!/bin/bash
# /etc/grub.d/01_theme_rotate — GRUB 主题开机自动轮换
# 生成时(grub-mkconfig 在 Linux 下运行本脚本):枚举所有主题目录,输出 if/elif 分支
# 运行时(GRUB 每次开机解析 grub.cfg):按 grubenv 计数器 theme_counter 轮换主题
# 新装主题后重新 grub-mkconfig 即自动加入轮换,无需改本脚本

cat <<'GRUB_HEAD'
load_env
if [ -z "${theme_counter}" ]; then set theme_counter=0; fi
GRUB_HEAD

i=0
for theme in /usr/share/grub/themes/*/theme.txt; do
  [ -f "$theme" ] || continue
  if [ "$i" -eq 0 ]; then
    echo "if [ \"\${theme_counter}\" = \"0\" ]; then"
  else
    echo "elif [ \"\${theme_counter}\" = \"$i\" ]; then"
  fi
  echo "  set theme=$theme"
  echo "  set theme_counter=$((i + 1))"
  i=$((i + 1))
done

# 计数器越界(轮换一圈后)归零,保证永远在主题数范围内
echo "else"
echo "  set theme_counter=0"
echo "fi"
cat <<'GRUB_TAIL'
save_env theme_counter
GRUB_TAIL

赋权并重新生成引导配置:

sudo chmod +x /etc/grub.d/01_theme_rotate   # 必须可执行,grub-mkconfig 才会运行它
sudo grub-mkconfig -o /boot/grub/grub.cfg

生成时终端会打印一行 # theme_rotate: N themes in rotation,看到它说明枚举成功——N 就是你当前的主题数量。

它做了什么? 对照脚本看两个阶段:

  • 生成期(grub-mkconfig 运行时):脚本枚举 /usr/share/grub/themes/ 下所有含 theme.txt 的主题目录,动态输出一串 if/elif 分支,每条分支对应一个主题,写进 grub.cfg。装了几个主题,就生成几条分支,全量参与轮换。
  • 运行期(开机时 GRUB 解析 grub.cfg):GRUB 执行 load_env 读入 grubenv 环境变量 → 按计数器 theme_counter 命中对应分支 set theme=... 选主题 → 计数器 +1 → save_env 写回 /boot/grub/grubenv → 下次开机读到的就是新值。计数器越界时归零,轮换一圈后从头开始。

细节说明:

  • 首次开机:计数器尚未设置,脚本把它置 0,落到字母序第一个主题;
  • 维护成本为零:以后装新主题到 /usr/share/grub/themes/,重跑一次 grub-mkconfig,新主题自动收进轮换链,/etc/grub.d/01_theme_rotate 本身一行都不用改:
sudo grub-mkconfig -o /boot/grub/grub.cfg   # 装新主题后重跑即可

为什么不做"真随机"?

我查过 GRUB 官方手册:random 模块只是加密库函数(用 ACPI PM Timer + TSC 做熵源),没有脚本命令可用;hexdump_random 仅用于调试打印;而且 GRUB 脚本既没有命令替换(date 的输出塞不进变量),expr.mod 也没安装,做不了算术随机。

所以退而求其次——顺序轮换:按字母序把全部主题过一遍再从头来。均匀、不重复、体验上跟随机几乎没差别,还比真随机多一个优点:轮换一圈正好把每个主题都看一遍,不会出现"某个主题一直轮不到"的尴尬。

双系统下也能生效(而且零成本)

我的是 Arch Linux + Windows 双系统,这套方案天然跨系统生效,因为:

  1. GRUB 引导是两个系统共用的;
  2. grub.cfg 在进入任何系统之前就执行了;
  3. 主题轮换发生在引导阶段,跟之后进的是 Windows 还是 Arch 没关系。

也就是说,进 Windows 前看到的主题照样轮换,Windows 侧零配置、零操作。唯一的代价是 Windows 那侧永远看不见好看的 GRUB 主题——但没关系,它不需要看。

小贴士:怎么保证"上次选谁,这次还是谁"

顺便提一个相关的双系统体验点:默认策略 GRUB_DEFAULT=saved + GRUB_SAVEDEFAULT=true 会让 GRUB 记住上次进入的系统——否则你每次进 Windows 后,下次开机菜单默认还是高亮 Arch。这个记忆在 Windows 系统更新自动重启时尤为有用:更新过程会自己重启好几次,如果没有记忆,每次重启都会停在 Arch 的菜单项上,你得守在旁边手动切回 Windows;有了它,Windows 更新全程自动,重启后直接回到 Windows 继续装。

注意一个坑:GRUB 的"记忆"靠的是菜单条目里的 save_env saved_entry 语句。系统自动生成的条目(10_linux)自带这段逻辑,但手写在 40_custom 里的条目不会自带。我当初手写的 Windows 入口就没有,结果每次进完 Windows,下次开机还是默认进 Arch。修复方式:给条目加个 --id,再补上保存逻辑:

menuentry "Windows" --id windows {
  search --fs-uuid 5FFC-0D5B   # 改成你 Windows EFI 分区的 UUID
  chainloader /EFI/Microsoft/Boot/bootmgfw.efi
  saved_entry=windows
  save_env saved_entry
}

改完照例 grub-mkconfig 重新生成。怎么判断自己有没有踩这个坑?看 /boot/grub/grubenv 的修改时间——如果它停留在安装系统那天,说明"记忆"从未生效过。

最后

从黑底白字到每次开机的惊喜,开机这件事终于有了点仪式感。整套方案的灵魂其实是那个"生成期枚举 + 运行期轮换"的思路——GRUB 的脚本机制比大多数人想象的要灵活。

如果哪天仓库更新了角色,git pull 一下新主题,cp 到主题目录,重跑一次 grub-mkconfig,主题池就扩容了。祝各位每天开机都能看到想看的那个角色。