** Description changed:

  Problem / 问题描述:
  
  The /etc/grub.d/10_linux script in Ubuntu forces the creation of
  top-level menu entries and hard-codes the position of the
  "Advanced options for Ubuntu" submenu directly after the main Ubuntu
  entry. There is no supported configuration to reorder top-level
  entries, to control which entries appear on the first page, or to
  rename them.
  
  Ubuntu 的 /etc/grub.d/10_linux 脚本强制生成顶层菜单项,并且把
  "Advanced options for Ubuntu" 子菜单固定插入在 Ubuntu 主条目之后。
  用户无法通过任何受支持的配置来调整顶层菜单的顺序、决定哪些条目
  显示在第一页,也无法重命名它们。
  
  A typical dual-boot user wants the first page to look like this:
  
  一个典型的双系统用户希望第一页菜单是这样的:
  
      Ubuntu
      Windows
      Restart
      Shutdown
      Advanced options for Ubuntu
      ...
  
  That is: Ubuntu, Windows, Restart, and Shutdown as top-level entries,
  with "Advanced options for Ubuntu" moved to the bottom (or hidden
  entirely). Ubuntu currently does not allow this.
  
  也就是说:Ubuntu、Windows、重启、关机作为平级顶层条目,高级选项
  移到底部(或干脆隐藏)。Ubuntu 当前不允许这种调整。
  
  Three concrete problems:
  
  三个具体问题:
  
  1. Top-level menu order is determined by script execution order and
     cannot be configured.
     Scripts in /etc/grub.d/ run in numeric filename order. 10_linux
     generates the Ubuntu entry, 30_os-prober generates the Windows
     entry. As a result, Ubuntu always appears before Windows. Users
     cannot swap them through configuration.
  
  1. 顶层菜单的顺序由脚本执行顺序决定,用户无法配置。
     /etc/grub.d/ 下的脚本按文件名数字顺序执行。10_linux 生成 Ubuntu
     条目,30_os-prober 生成 Windows 条目。结果是 Ubuntu 永远排在
     Windows 前面。用户无法通过配置交换它们的位置。
  
  2. The position and visibility of "Advanced options for Ubuntu" cannot
     be controlled.
     It is force-generated by 10_linux and fixed directly after the main
     Ubuntu entry. Users cannot move it to the bottom, cannot hide it,
     and cannot control the ordering of entries inside it.
  
  2. "Advanced options for Ubuntu" 的位置和显示无法控制。
     它由 10_linux 强制生成,固定插入在 Ubuntu 主条目之后。用户既
     不能把它移到底部,也不能隐藏它,更不能决定它内部条目的顺序。
  
  3. Menu entry names are hard-coded and cannot be renamed.
     Top-level names such as "Ubuntu", "Advanced options for Ubuntu",
     and "Windows Boot Manager" are written directly in scripts. Users
     cannot shorten or rename them through any supported configuration.
  
  3. 菜单项名称由脚本硬编码,无法重命名。
     "Ubuntu"、"Advanced options for Ubuntu"、"Windows Boot Manager"
     等顶层名称直接写在脚本里。用户无法通过任何受支持的配置来
     缩短或重命名它们。
  
  The only workaround is editing /etc/grub.d/10_linux directly, which is
  a system script and may be overwritten or broken by package upgrades.
  
  唯一的变通办法是直接编辑 /etc/grub.d/10_linux,但这是系统脚本,
  可能在软件包升级时被覆盖或引发错误。
  
  
  Why this matters / 为什么这个问题重要:
  
  This is not a cosmetic "nice to have". It affects the core experience
  of Ubuntu desktop users.
  
  这不是一个"锦上添花"的美化需求,而是影响 Ubuntu 桌面基本盘的
  体验问题。
  
  1. Dual-boot is a mainstream Ubuntu desktop scenario.
     A large share of Ubuntu users run it alongside Windows. For them,
     the boot menu is the first screen seen on every startup. Its order
     and content directly shape whether Ubuntu feels professional and
     controllable, or forced and messy.
  
  1. 双系统是 Ubuntu 桌面的主流使用场景。
     大量 Ubuntu 用户是"Windows 为主、Ubuntu 为辅"的双系统用户。
     对于这些用户,启动菜单是每次开机都会看到的第一屏。这一屏的
     顺序和内容,直接决定了 Ubuntu 给人的第一印象是"专业、可控"
     还是"混乱、被强迫"。
  
  2. The current design puts what users need least in the most prominent
     position.
     Most dual-boot users only need two entries 99% of the time:
     Windows and Ubuntu. Yet the current menu forces "Advanced options"
     into the top-level view, while entries users actually want, such as
     Restart and Shutdown, are not under unified ordering control. This
     is a misplacement of priorities.
  
  2. 当前设计把用户最不需要的东西放在了最显眼的位置。
     普通双系统用户 99% 的时间只需要两个选项:Windows 和 Ubuntu。
     但当前菜单强制把"Advanced options"塞进顶层视野,而用户真正
     想要的"重启""关机"等条目反而不受统一排序控制。这是优先级
     的错位。
  
  3. Users are forced to choose between "live with it" and "risk editing
     system scripts".
     With no supported configuration, users must either accept a menu
     that does not match their expectations, or modify a system script
     like /etc/grub.d/10_linux and accept upgrade risk. No mature
     desktop distribution should leave users in this position.
  
  3. 用户被迫在"忍受"和"冒险改脚本"之间二选一。
     官方不提供配置,用户要么接受一个不符合预期的菜单,要么去改
     /etc/grub.d/10_linux 这种系统脚本并承担升级风险。这不是一个
     成熟桌面发行版应该留给用户的处境。
  
  4. This directly weakens Ubuntu's appeal to dual-boot users.
     Windows gives users no customization at all. This is exactly where
     Ubuntu could differentiate itself. GRUB's script mechanism already
     supports splitting and reordering via filenames. Ubuntu instead
     hard-codes the logic inside 10_linux, giving up that advantage for
     no reason. Getting this right would give Windows users a real
     incentive to install Ubuntu for a better boot experience.
  
  4. 这直接削弱了 Ubuntu 对双系统用户的吸引力。
     Windows 的启动菜单完全不给自定义机会,这本来就是 Ubuntu 可以
     形成差异化优势的地方。GRUB 的脚本机制本身完全支持拆分和调序,
     Ubuntu 却选择把逻辑写死在 10_linux 里,等于主动放弃了这个优势。
     把这件事做好,对"Windows 用户为了更好的启动体验而愿意装一个
     Ubuntu"是有实际推动作用的。
  
  
  Suggested improvement / 改进建议:
  
  1. Split the submenu generation logic out of 10_linux into a separate
     script (e.g. 11_linux_submenu) so it can be enabled, disabled, or
     reordered using the existing /etc/grub.d/ numbering mechanism.
  
     More fundamentally: follow the Unix principle of "one script, one
     menu entry". Instead of 10_linux generating multiple top-level
     entries and a submenu all at once, each menu entry should be
     generated by its own script:
  
         10_linux          -> Ubuntu (current kernel)
         11_linux_submenu  -> Advanced options for Ubuntu
         30_os-prober      -> Windows (or its own dedicated script)
         40_custom         -> user-defined entries
         ...
  
     This is the lowest-cost, most self-contained change. It is almost
     a pure code move: the submenu block is already isolated inside
     10_linux, and GRUB's /etc/grub.d/ mechanism already supports
     enabling, disabling, and reordering scripts by filename. Very
     little testing is required beyond confirming that the generated
     grub.cfg is unchanged when the new scripts run in their default
     positions.
  
     Benefits:
  
     - Users can reorder top-level entries simply by renaming scripts
       (e.g. 09_linux before 30_os-prober), without editing system
       files or risking upgrade breakage.
     - Users can hide any entry by removing its executable bit
       (chmod -x) or renaming the script, instead of editing 10_linux.
     - Tools like Grub Customizer no longer need to "hijack" GRUB's
       script output to reorder or hide entries. They can simply
       rename, enable, or disable individual scripts. This would let
       such tools work with GRUB's native mechanism instead of against
       it, removing the upgrade risk that currently makes them
       fragile.
     - It aligns with the existing design of /etc/grub.d/, where
       scripts like 30_os-prober and 40_custom already each handle a
       distinct concern.
  
  1. 将子菜单生成逻辑从 10_linux 中拆分到单独脚本(例如
     11_linux_submenu),使其可以通过现有的 /etc/grub.d/ 编号机制
     启用、禁用或调整顺序。
  
     更根本地说:遵循 Unix 的"一个脚本只做一件事"原则,让每个菜单项
     由独立的脚本生成,而不是让 10_linux 一次性生成多个顶层条目和
     子菜单:
  
         10_linux          -> Ubuntu(当前内核)
         11_linux_submenu  -> Advanced options for Ubuntu
         30_os-prober      -> Windows(或它自己的专用脚本)
         40_custom         -> 用户自定义条目
         ...
  
     这是成本最低、最自包含的改动。它几乎只是一次纯粹的代码搬迁:
     子菜单块在 10_linux 内部本来就是一个独立部分,而 GRUB 的
     /etc/grub.d/ 机制本身就支持按文件名启用、禁用和调整顺序。除了
     确认新脚本在默认位置运行时生成的 grub.cfg 保持不变之外,几乎
     不需要额外测试。
  
     好处:
  
     - 用户只需重命名脚本(例如把 09_linux 排在 30_os-prober 前面)
       就能调整顶层条目的顺序,无需编辑系统文件,也不承担升级风险。
     - 用户可以通过移除可执行权限(chmod -x)或重命名脚本来隐藏
       任意条目,而不是去改 10_linux。
     - Grub Customizer 这类工具不再需要"劫持"GRUB 的脚本输出来
       调整顺序或隐藏条目。它们只需重命名、启用或禁用单个脚本即可。
       这让这类工具能够顺应 GRUB 的原生机制,而不是与之对抗,从而
       消除目前让它们如此脆弱的升级风险。
     - 这与 /etc/grub.d/ 的现有设计一致:30_os-prober 和 40_custom
       本来就各自处理一个独立的关注点。
  
  2. Allow users to rename menu entries through a simple mapping.
  
     The simplest and most maintainable approach is a name mapping,
     not a full localization system. Users should be able to define
     a mapping that overrides the display name of any menu entry:
  
         "Advanced options for Ubuntu" -> "高级选项"
         "Windows Boot Manager"        -> "Windows"
         "Ubuntu"                      -> "系统"
  
     This mapping can live in /etc/default/grub or a dedicated file
     such as /etc/grub.d/99_menu_labels. When 10_linux or os-prober
     generates an entry, the script checks the mapping and uses the
     user-supplied name if one exists.
  
     Benefits:
  
     - Users decide exactly what each entry is called, in any
       language, without editing system scripts.
     - No need to implement a complex locale-aware translation
       framework. It is a pure override: if no mapping exists, the
       original name is used.
     - It works for Ubuntu entries, the "Advanced options" submenu,
       and entries generated by os-prober (such as Windows).
  
  2. 允许用户通过简单的名称映射来重命名菜单项。
  
     最简单、最易维护的做法是名称映射,而不是一套完整的多语言
     系统。用户应能定义一份映射,覆盖任意菜单项的显示名称:
  
         "Advanced options for Ubuntu" -> "高级选项"
         "Windows Boot Manager"        -> "Windows"
         "Ubuntu"                      -> "系统"
  
     这份映射可以放在 /etc/default/grub 中,或一个专用文件如
     /etc/grub.d/99_menu_labels。当 10_linux 或 os-prober 生成
     条目时,脚本检查映射,如果存在用户指定的名称就使用它。
  
     好处:
  
     - 用户自行决定每个条目的名称,任何语言都行,无需编辑系统
       脚本。
     - 不需要实现复杂的按语言环境翻译的框架。这是纯粹的覆盖:
       没有映射就沿用原名。
     - 适用于 Ubuntu 条目、"Advanced options" 子菜单,以及由
       os-prober 生成的条目(如 Windows)。
  
  3. Provide a supported configuration mechanism for top-level menu
     order, for example through a variable in /etc/default/grub or a
     user-editable ordering file.
  
  3. 提供一个受支持的配置机制,让用户可以指定顶层菜单项的顺序,
     例如通过 /etc/default/grub 中的变量,或者通过一个用户可编辑的
     排序文件。
  
  4. Allow "Advanced options for Ubuntu" to be moved or hidden, for
     example via GRUB_ADVANCED_SUBMENU_POSITION=hidden|top|bottom.
  
  4. 允许用户隐藏或移动 "Advanced options for Ubuntu" 子菜单,例如
     提供 GRUB_ADVANCED_SUBMENU_POSITION=hidden|top|bottom 之类的
     配置项。
  
  5. Ideally, Restart and Shutdown entries should also be included in
     unified ordering control.
  
  5. 理想情况下,重启、关机等条目也应纳入统一的排序控制。
  
  This does not require changes to GNU GRUB upstream syntax. The problem
  is entirely in Ubuntu's own /etc/grub.d/ script structure. GRUB's
  script mechanism already supports splitting and reordering via
  filenames. Ubuntu simply chooses to hard-code the logic inside
  10_linux, leaving no configuration space for users.
  
  这不需要修改 GNU GRUB 上游的语法。问题完全出在 Ubuntu 自己的
  /etc/grub.d/ 脚本结构上。GRUB 的脚本机制本身已经支持通过文件名
  拆分和调整顺序,只是 Ubuntu 在这里选择了把逻辑写死在 10_linux
  内部,没有给用户留下配置空间。
  
  
+ Proof of feasibility / 可行性证明:
+ 
+ The attached script demonstrates that the requested feature is
+ implementable at the user level without changing GRUB upstream
+ syntax. It post-processes grub.cfg to rename and reorder entries,
+ and moves the "Advanced options for Ubuntu" submenu to the bottom.
+ This is not a substitute for a proper configuration mechanism in
+ Ubuntu itself — it is evidence that the gap is real and fillable.
+ 
+ 附件中的脚本证明,所请求的功能可以在用户层面实现,且无需修改
+ GRUB 上游语法。它对 grub.cfg 做后处理来重命名和重排条目,并把
+ "Advanced options for Ubuntu" 子菜单移到底部。这不是 Ubuntu 自身
+ 正规配置机制的替代品——它只是证明这个缺口真实存在且可以填补。
+ 
+ 
  Impact / 影响范围:
  
  Any Ubuntu user with multiple kernels installed is affected,
  especially dual-boot users who care about the clarity and
  predictability of the boot menu. The boot menu is the first screen
  seen on every startup. Its order and content should be controlled by
  the user, not hard-coded by a script.
  
  任何安装了多个内核的 Ubuntu 用户都会受到影响,尤其是关心启动菜单
  清晰度和可预测性的双系统用户。启动菜单是每次开机都会看到的第一屏,
  它的顺序和内容应该由用户掌控,而不是被脚本强制绑定。

** Attachment added: "#!/bin/bash # # grub-menu-reorder.sh # # Purpose / 用途: #  
 Post-process /boot/grub/grub.cfg to rename top-level menu entries, #   reorder 
them according to a user-defined rank, and move the #   "Advanced options for 
Ubuntu" submenu to the bottom. # #   对 /boot/grub/grub.cfg 做后处理:重命名顶层菜单项,按用户 #  
 定义的优先级重排序,并把 "Advanced options for Ubuntu" 子菜单 #   移到底部。 # # Why this exists / 
为什么需要这个脚本: #   Ubuntu's 10_linux script hard-codes entry order and labels, and 
#   provides no supported configuration to change them. This script #   is a 
user-side workaround that operates on the generated file, #   not on the 
scripts themselves. It does NOT hijack /etc/grub.d/ #   or replace any system 
script. It only rewrites grub.cfg after #   update-grub has run. # #   Ubuntu 的 
10_linux 脚本硬编码了菜单顺序和名称,没有提供任何 #   受支持的配置来修改它们。这个脚本是用户侧的变通方案,它只对 #   
生成后的文件做处理,不碰 /etc/grub.d/ 下的脚本,也不替换任何 #   系统脚本。它只在 update-grub 运行之后重写 grub.cfg。 
# # What it does / 它做了什么: #   1. Backs up grub.cfg before any modification. #   
2. Renames matching menu entries using a mapping table. #   3. Reorders 
top-level entries according to a rank value. #   4. Pushes "Advanced options 
for Ubuntu" to the bottom. #   5. Validates the result with grub-script-check 
before replacing. # #   1. 修改前备份 grub.cfg。 #   2. 使用映射表重命名匹配的菜单项。 #   3. 按 rank 
值重排顶层条目。 #   4. 把 "Advanced options for Ubuntu" 推到底部。 #   5. 用 
grub-script-check 校验通过后再替换。 # # This is a proof that the feature is 
implementable at the user # level. It is not a substitute for a proper 
configuration mechanism # in Ubuntu itself. # # 这证明了该功能在用户层面是可以实现的。它不能替代 Ubuntu 
自身 # 应当提供的正规配置机制。 #  set -euo pipefail  GRUB_CFG=/boot/grub/grub.cfg 
BACKUP="${GRUB_CFG}.hijack.bak" LOG=/var/log/grub-hijack.log 
RULES=/tmp/grub-hijack.rules  log() { echo "[$(date +%F\ %T)] $*" >> "$LOG"; }  
RULES_ARR=(   "Ubuntu|Ubuntu|1"   "Windows Boot Manager (on 
/dev/sda1)|Windows|2"   "Restart|Restart|3"   "Shutdown|Shutdown|3"   "UEFI 
Firmware Settings|UEFI Firmware Settings|5"   "Advanced options for 
Ubuntu|Advanced options for Ubuntu|6" )  [ -w "$GRUB_CFG" ] || { log "SKIP: not 
writable"; exit 0; }  cp -f "$GRUB_CFG" "$BACKUP" || { log "ERROR: backup 
failed"; exit 1; } : > "$RULES" for r in "${RULES_ARR[@]}"; do printf '%s\n' 
"$r" >> "$RULES"; done  if ! awk -v rules_file="$RULES" ' function title_of(ln, 
  pre,t,q) {   pre = index(ln, SQ); if (pre==0) return ""   t = substr(ln, 
pre+1); q = index(t, SQ); if (q==0) return t   return substr(t, 1, q-1) } 
function rename_line(ln, newt,   pre,rest,q) {   pre = index(ln, SQ); if 
(pre==0) return ln   rest = substr(ln, pre+1); q = index(rest, SQ); if (q==0) 
return ln   return substr(ln, 1, pre) newt substr(rest, q) } BEGIN {   SQ = 
sprintf("%c", 39)   blkpat = "^[ \t]*(menuentry|submenu) " SQ    n_rules = 0   
while ((getline rl < rules_file) > 0) {     if (rl == "") continue     m = 
split(rl, f, "|"); if (m < 3) continue     n_rules++; old_a[n_rules]=f[1]; 
new_a[n_rules]=f[2]; rank_a[n_rules]=f[3]+0   }   close(rules_file)    fn = 
ARGV[1]; ARGV[1] = ""   c = 0   while ((getline line < fn) > 0) lines[++c] = 
line   close(fn)    nb = 0; seg = 0; i = 1; seg_start = 0   while (i <= c) {    
 if (lines[i] ~ /^### BEGIN \/etc\/grub.d\//) {       if (seg > 0) { nb++; 
b_start[nb]=seg_start; b_end[nb]=i-1; b_title[nb]="(segment)" }       seg = 1; 
seg_start = i; i++; continue     }     if (lines[i] ~ /^### END 
\/etc\/grub.d\//) {       if (seg > 0) { nb++; b_start[nb]=seg_start; 
b_end[nb]=i; b_title[nb]="(segment)"; seg=0 }       i++; continue     }     i++ 
  }   if (seg > 0) { nb++; b_start[nb]=seg_start; b_end[nb]=c; 
b_title[nb]="(segment)" }    nb2 = 0   for (b = 1; b <= nb; b++) {     adv = 0  
   for (k = b_start[b]; k <= b_end[b]; k++)       if (lines[k] ~ blkpat && 
title_of(lines[k]) ~ /^Advanced options/) { adv = k; break }     if (adv > 0) { 
      nb2++; b2_start[nb2]=b_start[b]; b2_end[nb2]=adv-1; 
b2_title[nb2]="(ubuntu-part)"       nb2++; b2_start[nb2]=adv; 
b2_end[nb2]=b_end[b]; b2_title[nb2]="Advanced options for Ubuntu"     } else {  
     nb2++; b2_start[nb2]=b_start[b]; b2_end[nb2]=b_end[b]; 
b2_title[nb2]=b_title[b]     }   }   nb = nb2   for (b = 1; b <= nb; b++) { 
b_start[b]=b2_start[b]; b_end[b]=b2_end[b]; b_title[b]=b2_title[b] }    for (b 
= 1; b <= nb; b++) {     bt = b_title[b]     if (bt == "(segment)" || bt == 
"(ubuntu-part)") {       for (k = b_start[b]; k <= b_end[b]; k++)         if 
(lines[k] ~ blkpat) { bt = title_of(lines[k]); break }       if (bt == "") bt = 
"(segment)"       b_title[b] = bt     }     for (k = 1; k <= n_rules; k++) if 
(bt == old_a[k]) {       if (new_a[k] != old_a[k]) {         for (k2 = 
b_start[b]; k2 <= b_end[b]; k2++)           if (lines[k2] ~ blkpat) { lines[k2] 
= rename_line(lines[k2], new_a[k]); break }       }       rk = rank_a[k]       
if ((b in b_rank) == 0 || rk < b_rank[b]) b_rank[b] = rk     }   }    out_n = 
0; done = 0   while (done < nb) {     minr = 999999     for (b = 1; b <= nb; 
b++)       if (cfg_out[b] == 0 && (b in b_rank) != 0 && b_rank[b] < minr) minr 
= b_rank[b]     if (minr == 999999) break     for (b = 1; b <= nb; b++)       
if (cfg_out[b] == 0 && (b in b_rank) != 0 && b_rank[b] == minr) { out_n++; 
out[out_n]=b; cfg_out[b]=1; done++ }   }   for (b = 1; b <= nb; b++) if ((b in 
b_rank) == 0) { out_n++; out[out_n]=b }   for (o = 1; o <= out_n; o++) { b = 
out[o]; for (k = b_start[b]; k <= b_end[b]; k++) print lines[k] } } ' 
"$GRUB_CFG" > "${GRUB_CFG}.tmp"; then   log "ERROR: awk failed, restored"   cp 
-f "$BACKUP" "$GRUB_CFG"   rm -f "${GRUB_CFG}.tmp"   exit 1 fi  [ -s 
"${GRUB_CFG}.tmp" ] || {   log "ERROR: empty, restored"   cp -f "$BACKUP" 
"$GRUB_CFG"   rm -f "${GRUB_CFG}.tmp"   exit 1 }  grub-script-check 
"${GRUB_CFG}.tmp" >/dev/null 2>&1 || {   log "ERROR: syntax invalid, restored"  
 cp -f "$BACKUP" "$GRUB_CFG"   rm -f "${GRUB_CFG}.tmp"   exit 1 }  mv -f 
"${GRUB_CFG}.tmp" "$GRUB_CFG" log "OK: done (rules: ${#RULES_ARR[@]})""
   
https://bugs.launchpad.net/ubuntu/+source/grub2/+bug/2167721/+attachment/6001056/+files/grub-hijack.sh

-- 
You received this bug notification because you are a member of Ubuntu
Bugs, which is subscribed to Ubuntu.
https://bugs.launchpad.net/bugs/2167721

Title:
  [Wishlist] Allow users to control GRUB top-level menu order and
  content (Ubuntu / Windows / Restart / Shutdown / Advanced options)

To manage notifications about this bug go to:
https://bugs.launchpad.net/ubuntu/+source/grub2/+bug/2167721/+subscriptions


-- 
ubuntu-bugs mailing list
[email protected]
https://lists.ubuntu.com/mailman/listinfo/ubuntu-bugs

Reply via email to