#!/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[@]})"

** Attachment added: "个人解决方案,但每次更新都被覆盖。"
   
https://bugs.launchpad.net/ubuntu/+source/grub2/+bug/2167721/+attachment/6001057/+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