本次修改了三个地方:

① set_boot_kernel(去掉确认,新增禁止自动选最新内核)

移除 read -p 确认,直接写入 /etc/default/grub
同时将 GRUB_SAVEDEFAULT 强制写为 false,防止 GRUB 记住上次选择 干扰指定内核
新增 UUID 有效性检查,获取失败时报错退出而非写入空值
② hold_default_boot_kernel(新增锁定元包)

原来只锁定具体版本的 linux-image-6.8.0-52-generic
新增锁定 linux-image-generic 和 linux-headers-generic 两个元包——这才是 apt 升级时切换内核的根本原因
③ 主流程(去掉两个交互确认)

安装完成后自动调用 set_default_boot_kernel + hold_default_boot_kernel,无需用户输入
This commit is contained in:
beyondx123
2026-05-15 17:42:40 +08:00
parent c0f38632db
commit ca1c38417c
2 changed files with 74 additions and 53 deletions
+44 -31
View File
@@ -2,18 +2,18 @@
## 版本:V1.3(支持离线安装 + DKMS 故障处理 + 自动日志记录) ## 版本:V1.3(支持离线安装 + DKMS 故障处理 + 自动日志记录)
--- ***
## 功能特性 ## 功能特性
**在线/离线双模式**:自动检测网络,无网时切换离线模式 **在线/离线双模式**:自动检测网络,无网时切换离线模式\
**DKMS 智能处理**:自动检测问题驱动,提供交互式修复 **DKMS 智能处理**:自动检测问题驱动,提供交互式修复\
**预下载支持**:在有网机器上打包,传到离线机器安装 **预下载支持**:在有网机器上打包,传到离线机器安装\
**驱动自动编译**:网卡、显卡驱动随内核自动重编译 **驱动自动编译**:网卡、显卡驱动随内核自动重编译\
**版本锁定**:防止系统自动更新覆盖指定内核 **版本锁定**:防止系统自动更新覆盖指定内核\
**自动日志记录**:所有操作追加写入 `log-日期-dacs.log` **自动日志记录**:所有操作追加写入 `log-日期-dacs.log`
--- ***
## 日志功能 ## 日志功能
@@ -22,6 +22,7 @@
**日志文件命名:** `log-YYYYMMDD-dacs.log`(例如:`log-20240514-dacs.log` **日志文件命名:** `log-YYYYMMDD-dacs.log`(例如:`log-20240514-dacs.log`
**日志内容包括:** **日志内容包括:**
- 脚本启动/完成时间 - 脚本启动/完成时间
- 执行的命令和参数 - 执行的命令和参数
- 所有关键操作的输出(apt-get、dpkg、dkms 等) - 所有关键操作的输出(apt-get、dpkg、dkms 等)
@@ -29,6 +30,7 @@
- 系统信息(主机名、当前内核等) - 系统信息(主机名、当前内核等)
**查看日志:** **查看日志:**
```bash ```bash
# 实时查看日志 # 实时查看日志
tail -f log-$(date +%Y%m%d)-dacs.log tail -f log-$(date +%Y%m%d)-dacs.log
@@ -40,7 +42,7 @@ less log-$(date +%Y%m%d)-dacs.log
grep -i error log-*.log grep -i error log-*.log
``` ```
--- ***
## 使用场景 ## 使用场景
@@ -49,19 +51,20 @@ grep -i error log-*.log
**适用:** 目标机器有网络连接 **适用:** 目标机器有网络连接
```bash ```bash
sudo ./switch_kernel_V1.2.sh sudo ./switch_kernel.sh
# 或指定版本 # 或指定版本
sudo ./switch_kernel_V1.2.sh --version 6.8.0-52-generic sudo ./switch_kernel.sh --version 6.8.0-52-generic
``` ```
**流程:** **流程:**
1. 自动检测网络 ✓ 1. 自动检测网络 ✓
2. 检查 DKMS 驱动兼容性 2. 检查 DKMS 驱动兼容性
3. 在线下载并安装内核 3. 在线下载并安装内核
4. 自动编译驱动(DKMS 4. 自动编译驱动(DKMS
5. 配置 GRUB 引导 5. 配置 GRUB 引导
--- ***
### 场景 2:离线安装(两步法) ### 场景 2:离线安装(两步法)
@@ -70,10 +73,11 @@ sudo ./switch_kernel_V1.2.sh --version 6.8.0-52-generic
#### 步骤 1:在有网机器上下载包 #### 步骤 1:在有网机器上下载包
```bash ```bash
sudo ./switch_kernel_V1.2.sh --download /tmp/kernel-debs --version 6.8.0-52-generic sudo ./switch_kernel.sh --download ./dacs --version 6.8.0-52-generic
``` ```
**输出:** **输出:**
``` ```
[OK] 下载完成,包列表: [OK] 下载完成,包列表:
-rw-r--r-- 1 root root 14M linux-image-6.8.0-52-generic_6.8.0-52.53_amd64.deb -rw-r--r-- 1 root root 14M linux-image-6.8.0-52-generic_6.8.0-52.53_amd64.deb
@@ -82,7 +86,7 @@ sudo ./switch_kernel_V1.2.sh --download /tmp/kernel-debs --version 6.8.0-52-gene
-rw-r--r-- 1 root root 108M linux-modules-extra-6.8.0-52-generic_6.8.0-52.53_amd64.deb -rw-r--r-- 1 root root 108M linux-modules-extra-6.8.0-52-generic_6.8.0-52.53_amd64.deb
将 /tmp/kernel-debs 目录复制到目标机器后,运行: 将 /tmp/kernel-debs 目录复制到目标机器后,运行:
sudo ./switch_kernel_V1.2.sh --offline /tmp/kernel-debs --version 6.8.0-52-generic sudo ./switch_kernel.sh --offline ./dacs --version 6.8.0-52-generic
``` ```
#### 步骤 2:在目标机器上安装 #### 步骤 2:在目标机器上安装
@@ -92,37 +96,38 @@ sudo ./switch_kernel_V1.2.sh --download /tmp/kernel-debs --version 6.8.0-52-gene
scp -r /tmp/kernel-debs user@target-machine:/tmp/ scp -r /tmp/kernel-debs user@target-machine:/tmp/
# 在目标机器上运行 # 在目标机器上运行
sudo ./switch_kernel_V1.2.sh --offline /tmp/kernel-debs --version 6.8.0-52-generic sudo ./switch_kernel.sh --offline ./dacs --version 6.8.0-52-generic
``` ```
--- ***
### 场景 3:跳过 DKMS 编译(解决驱动兼容问题) ### 场景 3:跳过 DKMS 编译(解决驱动兼容问题)
**适用:** 已知某些驱动与新内核不兼容,想先安装内核 **适用:** 已知某些驱动与新内核不兼容,想先安装内核
```bash ```bash
sudo ./switch_kernel_V1.2.sh --skip-dkms sudo ./switch_kernel.sh --skip-dkms
``` ```
**效果:** **效果:**
- 临时禁用 DKMS 自动编译钩子 - 临时禁用 DKMS 自动编译钩子
- 内核安装完成后自动恢复钩子 - 内核安装完成后自动恢复钩子
- 可重启后手动编译驱动:`sudo dkms autoinstall -k 6.8.0-52-generic` - 可重启后手动编译驱动:`sudo dkms autoinstall -k 6.8.0-52-generic`
--- ***
## 参数说明 ## 参数说明
| 参数 | 说明 | 示例 | | 参数 | 说明 | 示例 |
|------|------|------| | ----------------- | ------------- | ---------------------------- |
| `--version <版本>` | 指定内核版本 | `--version 6.8.0-52-generic` | | `--version <版本>` | 指定内核版本 | `--version 6.8.0-52-generic` |
| `--download <目录>` | 仅下载包到目录(需要网络) | `--download /tmp/debs` | | `--download <目录>` | 仅下载包到目录(需要网络) | `--download /tmp/debs` |
| `--offline <目录>` | 强制离线模式,从目录安装 | `--offline /tmp/debs` | | `--offline <目录>` | 强制离线模式,从目录安装 | `--offline /tmp/debs` |
| `--skip-dkms` | 跳过 DKMS 自动编译 | `--skip-dkms` | | `--skip-dkms` | 跳过 DKMS 自动编译 | `--skip-dkms` |
| `-h, --help` | 显示帮助信息 | `-h` | | `-h, --help` | 显示帮助信息 | `-h` |
--- ***
## DKMS 故障处理 ## DKMS 故障处理
@@ -162,7 +167,7 @@ sudo ./switch_kernel_V1.2.sh --skip-dkms
- **B**:强制完成内核安装,忽略驱动编译错误(驱动可能不可用) - **B**:强制完成内核安装,忽略驱动编译错误(驱动可能不可用)
- **C**:退出脚本,查看 `TROUBLESHOOTING.md` 手动排查 - **C**:退出脚本,查看 `TROUBLESHOOTING.md` 手动排查
--- ***
## 常见问题 ## 常见问题
@@ -201,12 +206,14 @@ uname -r
### Q5: elkeid-driver 编译失败怎么办? ### Q5: elkeid-driver 编译失败怎么办?
**临时移除(推荐):** **临时移除(推荐):**
```bash ```bash
sudo dkms remove elkeid-driver/1.7.0.22 --all sudo dkms remove elkeid-driver/1.7.0.22 --all
sudo ./switch_kernel_V1.2.sh sudo ./switch_kernel_V1.2.sh
``` ```
**安装完成后恢复:** **安装完成后恢复:**
```bash ```bash
sudo apt-get install --reinstall elkeid-driver sudo apt-get install --reinstall elkeid-driver
# 或手动编译 # 或手动编译
@@ -216,6 +223,7 @@ sudo dkms install elkeid-driver/1.7.0.22 -k 6.8.0-52-generic
### Q6: 显卡驱动失效怎么办? ### Q6: 显卡驱动失效怎么办?
**NVIDIA 显卡:** **NVIDIA 显卡:**
```bash ```bash
# 检查驱动状态 # 检查驱动状态
nvidia-smi nvidia-smi
@@ -228,7 +236,7 @@ sudo dkms install nvidia/xxx -k 6.8.0-52-generic
**注意:** 如果启用了 Secure Boot,需要关闭或签名驱动模块。 **注意:** 如果启用了 Secure Boot,需要关闭或签名驱动模块。
--- ***
## 完整示例 ## 完整示例
@@ -266,7 +274,7 @@ sudo ./switch_kernel_V1.2.sh --skip-dkms --version 6.8.0-52-generic
sudo dkms autoinstall -k 6.8.0-52-generic sudo dkms autoinstall -k 6.8.0-52-generic
``` ```
--- ***
## 日志与调试 ## 日志与调试
@@ -302,11 +310,12 @@ dmesg | tail -100
参见 `TROUBLESHOOTING.md` 文档。 参见 `TROUBLESHOOTING.md` 文档。
--- ***
## 安全提示 ## 安全提示
⚠️ **Secure Boot**:如果启用了 Secure Boot,第三方驱动(NVIDIA、VirtualBox 等)可能无法加载。建议: ⚠️ **Secure Boot**:如果启用了 Secure Boot,第三方驱动(NVIDIA、VirtualBox 等)可能无法加载。建议:
- 关闭 Secure BootBIOS 设置) - 关闭 Secure BootBIOS 设置)
- 或为驱动模块签名(高级) - 或为驱动模块签名(高级)
@@ -314,11 +323,12 @@ dmesg | tail -100
⚠️ **生产环境**:建议先在测试环境验证,确认无问题后再部署到生产。 ⚠️ **生产环境**:建议先在测试环境验证,确认无问题后再部署到生产。
--- ***
## 更新日志 ## 更新日志
### V1.3 (2024-05-14) ### V1.3 (2024-05-14)
- ✨ 新增 DKMS 兼容性预检查 - ✨ 新增 DKMS 兼容性预检查
- ✨ 新增 DKMS 编译失败自动修复 - ✨ 新增 DKMS 编译失败自动修复
- ✨ 新增 `--skip-dkms` 参数 - ✨ 新增 `--skip-dkms` 参数
@@ -327,13 +337,16 @@ dmesg | tail -100
- 📝 添加详细的故障排查文档 - 📝 添加详细的故障排查文档
### V1.2 (2024-05-14) ### V1.2 (2024-05-14)
- ✨ 新增离线安装支持 - ✨ 新增离线安装支持
- ✨ 新增 `--download` 模式 - ✨ 新增 `--download` 模式
- ✨ 新增网络自动检测 - ✨ 新增网络自动检测
- 🐛 修复依赖安装顺序问题 - 🐛 修复依赖安装顺序问题
### V1.0 ### V1.0
- 🎉 初始版本 - 🎉 初始版本
- ✅ 在线安装内核 - ✅ 在线安装内核
- ✅ DKMS 驱动自动编译 - ✅ DKMS 驱动自动编译
- ✅ GRUB 引导配置 - ✅ GRUB 引导配置
+30 -22
View File
@@ -444,28 +444,34 @@ get_kernel_uuid() {
set_boot_kernel() { set_boot_kernel() {
local grub_path="/etc/default/grub" local grub_path="/etc/default/grub"
local default="GRUB_DEFAULT="
local newlines="" local newlines=""
local uuid_list local uuid_list
uuid_list=$(get_kernel_uuid) uuid_list=$(get_kernel_uuid)
if [[ -z "$uuid_list" || "$uuid_list" == ">" ]]; then
log_err "无法从 grub.cfg 中获取内核 UUID,请确认内核已安装并运行过 update-grub。"
return 1
fi
# 重写 GRUB_DEFAULT,同时确保 GRUB_SAVEDEFAULT 不干扰
while read -r l; do while read -r l; do
if [[ $l == $default* ]]; then if [[ $l == GRUB_DEFAULT=* ]]; then
newlines+="$default\"$uuid_list\"\n" newlines+="GRUB_DEFAULT=\"$uuid_list\"\n"
elif [[ $l == GRUB_SAVEDEFAULT=* ]]; then
newlines+="GRUB_SAVEDEFAULT=false\n"
else else
newlines+="$l\n" newlines+="$l\n"
fi fi
done < "$grub_path" done < "$grub_path"
echo -e "\n${YELLOW}新的 /etc/default/grub 配置预览:${NC}" # 如果文件里没有 GRUB_SAVEDEFAULT 这行,追加一条
echo -e "$newlines" if ! grep -q "^GRUB_SAVEDEFAULT=" "$grub_path"; then
newlines+="GRUB_SAVEDEFAULT=false\n"
read -p "请确认配置是否正确,输入Y确认回写文件,按任意键跳过: " answer
if [[ "$answer" == "Y" || "$answer" == "y" ]]; then
echo -e "$newlines" > "$grub_path"
return 0
fi fi
return 1
log_info "写入 GRUB_DEFAULT: $uuid_list"
echo -e "$newlines" > "$grub_path"
return 0
} }
set_default_boot_kernel() { set_default_boot_kernel() {
@@ -482,7 +488,6 @@ set_default_boot_kernel() {
log_info "正在更新 GRUB 引导程序..." log_info "正在更新 GRUB 引导程序..."
update-grub 2>&1 | tee -a "$LOG_FILE" update-grub 2>&1 | tee -a "$LOG_FILE"
# 如果没有跳过 DKMS,尝试为新内核编译驱动
if [[ "$SKIP_DKMS" == false ]]; then if [[ "$SKIP_DKMS" == false ]]; then
log_info "正在为新内核 ($KERNEL_VERSION) 构建驱动程序 (DKMS)..." log_info "正在为新内核 ($KERNEL_VERSION) 构建驱动程序 (DKMS)..."
if dkms autoinstall -k "$KERNEL_VERSION" 2>&1 | tee -a "$LOG_FILE"; then if dkms autoinstall -k "$KERNEL_VERSION" 2>&1 | tee -a "$LOG_FILE"; then
@@ -494,14 +499,22 @@ set_default_boot_kernel() {
else else
log_info "已跳过 DKMS 自动编译(可重启后手动运行: sudo dkms autoinstall -k $KERNEL_VERSION)。" log_info "已跳过 DKMS 自动编译(可重启后手动运行: sudo dkms autoinstall -k $KERNEL_VERSION)。"
fi fi
else
log_err "GRUB 配置写入失败,请手动检查 /etc/default/grub。"
exit 1
fi fi
} }
hold_default_boot_kernel() { hold_default_boot_kernel() {
log_info "正在锁定内核版本,防止被系统更新替换..." log_info "正在锁定内核版本,防止被系统更新替换..."
# 锁定具体版本包
apt-mark hold linux-image-"$KERNEL_VERSION" apt-mark hold linux-image-"$KERNEL_VERSION"
apt-mark hold linux-headers-"$KERNEL_VERSION" apt-mark hold linux-headers-"$KERNEL_VERSION"
apt-mark hold linux-modules-extra-"$KERNEL_VERSION" apt-mark hold linux-modules-extra-"$KERNEL_VERSION"
# 锁定元包,防止 apt 升级时将默认内核切换到新版本
apt-mark hold linux-image-generic
apt-mark hold linux-headers-generic
log_ok "内核版本已锁定。"
} }
# --- 主程序 --- # --- 主程序 ---
@@ -607,17 +620,12 @@ else
fi fi
fi fi
# 设置默认启动内核 # 安装成功后:自动设置默认启动内核 + 锁定版本(无需用户确认)
read -p "是否设置 $KERNEL_VERSION 为默认启动内核(Y/N): " answer log_step "设置 $KERNEL_VERSION 为默认启动内核..."
if [[ "$answer" == "Y" || "$answer" == "y" ]]; then set_default_boot_kernel
set_default_boot_kernel
fi
# 锁定内核版本 log_step "锁定内核版本,防止 apt 自动升级切换..."
read -p "是否锁定该内核版本(防止系统自动更新内核)?(Y/N): " answer hold_default_boot_kernel
if [[ "$answer" == "Y" || "$answer" == "y" ]]; then
hold_default_boot_kernel
fi
log_ok "操作完成!" log_ok "操作完成!"
echo -e "${YELLOW}注意:如果重启后显卡驱动不正常,请检查 Secure Boot 是否已关闭。${NC}" echo -e "${YELLOW}注意:如果重启后显卡驱动不正常,请检查 Secure Boot 是否已关闭。${NC}"