T7、Alternatives
Alternatives:多实现选择与主从链接管理
文档版本:3.0。最后核验日期:2026-10-15。适用对象:系统管理员、应用运维和软件包维护者。
1、文档说明与快速定位
Alternatives 管理“同一个命令由哪个已安装实现提供”。它保存候选、优先级和选择模式,再维护符号链接;不安装软件、不校验接口兼容、不迁移数据,也不替代服务重启或业务验收。
| 客户任务 | 章节 |
|---|---|
| 在独立示例中完成注册、切换与业务验证 | 第 2 节,随后第 10.3 节回收 |
| 理解机制、模式及删除行为 | 第 3~6 节 |
| 判断当前主机属于哪套实现 | 第 7 节 |
| 查语法及常用命令 | 第 8、13 节 |
| 处理 Java、MTA、Python 及选择迁移 | 第 10 节 |
| 处理断链或实际执行版本不符 | 第 11、14 节 |
| 执行约定 | 内容 |
|---|---|
| 主机与账号 | 目标 Linux 测试机的 root Bash 终端;先用现有管理员账号执行 sudo -i 进入。不在生产机首次演练 |
| 工作目录 | 命令使用绝对路径,不依赖当前目录;保持每次操作仅一名变更人员执行 |
| 实现选择 | 脚本优先使用本机 alternatives,否则使用 update-alternatives;不下载替换系统工具 |
| 独立示例 | 专属组 drafts-report,入口 /usr/local/bin/drafts-report,制品 /opt/alternatives-demo;不接管已有应用或发行版链接组 |
| 输入来源 | 第 2.1 节创建的两个固定示例版本、配置和手册;均由管理员维护,示例程序读取同组的配置链接 |
| 执行门禁 | 每块退出码为 0 且出现末行唯一 _OK 标记才继续;文件状态不符、命令非零或标记缺失时停止 |
| 重复执行 | 初始化拒绝已有目录;注册和选择操作可重复,具体状态条件见各步骤;导出拒绝覆盖首次记录 |
| 生效与超时 | 本地命令正常应在数秒内结束;超过 30 秒无响应时停止后续步骤并检查进程、文件系统及其他变更。没有后台服务等待环节 |
| 恢复 | 本次新建组按第 10.3 节撤销;已有业务组必须先保留原模式、候选路径及业务验证方法,不能套用示例的整组删除 |
六种原生系统隔离副本的实际包版本与验收边界见第 7 节及 GA 验收记录。本次不将 RHEL、SLES、未测试发行版或客户现场签字计入已验收范围。
2、快速入门
2.1、检查环境并创建示例制品
1、在目标测试机 root 终端执行前置检查。/opt/alternatives-demo、入口、主从名称均必须未被占用;即使已有内容看似相同,也先检查其所有者,不直接覆盖。不要设置改变 dpkg 根目录或数据库路径的环境变量。
bash <<'ALT_PREFLIGHT'
set -Eeuo pipefail
printf '[检查] 执行身份、系统工具和专属路径\n'
test "$(id -u)" -eq 0
test -z "${DPKG_ROOT-}${DPKG_ADMINDIR-}${DPKG_INSTDIR-}"
ALT=$(command -v alternatives || command -v update-alternatives)
"$ALT" --version
for path in /opt/alternatives-demo /usr/local/bin/drafts-report /usr/local/share/man/man1/drafts-report.1; do
test ! -e "$path"
test ! -L "$path"
done
for name in drafts-report drafts-report.conf drafts-report.1; do
for directory in /etc/alternatives /var/lib/alternatives /var/lib/dpkg/alternatives; do
test ! -e "$directory/$name"
test ! -L "$directory/$name"
done
done
printf 'ALT_PREFLIGHT_OK\n'
ALT_PREFLIGHT
2、创建两个完整示例版本。v1、v2 是此业务样本的固定版本,不是 JDK 或操作系统版本。管理员创建并维护整个 /opt/alternatives-demo;示例程序会读取 report.conf,手册由 man 读取,第 2.2 节才创建这些入口。OWNER 标识本次示例归属;初始化只允许首次执行,中途失败按第 10.3 节检查和回收后重做。
bash <<'ALT_PREPARE'
set -Eeuo pipefail
printf '[创建] drafts-report v1/v2、配置及手册\n'
test "$(id -u)" -eq 0
ROOT=/opt/alternatives-demo
test ! -e "$ROOT"
test ! -L "$ROOT"
install -d -m 0755 "$ROOT"
printf 'drafts-alternatives-demo-v1\n' > "$ROOT/OWNER"
for version in v1 v2; do
install -d -m 0755 "$ROOT/$version"
cat > "$ROOT/$version/report" <<ALT_PROGRAM
#!/bin/sh
set -eu
test "\$(cat /opt/alternatives-demo/report.conf)" = 'format=$version'
printf 'report=$version\\n'
ALT_PROGRAM
printf 'format=%s\n' "$version" > "$ROOT/$version/report.conf"
printf '.TH DRAFTS-REPORT 1\n.SH NAME\ndrafts-report \\- sample %s\n' "$version" > "$ROOT/$version/report.1"
chmod 0755 "$ROOT/$version/report"
chmod 0644 "$ROOT/$version/report.conf" "$ROOT/$version/report.1"
done
printf 'ALT_PREPARE_OK\n'
ALT_PREPARE
2.2、注册主从链接并验证自动选择
1、主入口是 /usr/local/bin/drafts-report,从链接是配置和手册。注册命令负责创建两层链接;不再手工执行 ln -sf。六个平台都支持下面的 --slave 拼写,部分新版 chkconfig 将其显示为 follower。
bash <<'ALT_REGISTER'
set -Eeuo pipefail
export LC_ALL=C
printf '[注册] drafts-report 主链接、配置和手册\n'
ROOT=/opt/alternatives-demo
ALT=$(command -v alternatives || command -v update-alternatives)
test "$(cat "$ROOT/OWNER")" = drafts-alternatives-demo-v1
install -d -m 0755 /usr/local/bin /usr/local/share/man/man1
for version in v1 v2; do
priority=100
if test "$version" = v2; then priority=200; fi
"$ALT" --install /usr/local/bin/drafts-report drafts-report "$ROOT/$version/report" "$priority" \
--slave "$ROOT/report.conf" drafts-report.conf "$ROOT/$version/report.conf" \
--slave /usr/local/share/man/man1/drafts-report.1 drafts-report.1 "$ROOT/$version/report.1"
done
"$ALT" --display drafts-report
test "$(readlink /usr/local/bin/drafts-report)" = /etc/alternatives/drafts-report
test "$(readlink -f /usr/local/bin/drafts-report)" = "$ROOT/v2/report"
test "$(readlink -f "$ROOT/report.conf")" = "$ROOT/v2/report.conf"
test "$(readlink -f /usr/local/share/man/man1/drafts-report.1)" = "$ROOT/v2/report.1"
test "$(/usr/local/bin/drafts-report)" = report=v2
printf 'ALT_REGISTER_OK\n'
ALT_REGISTER
首次注册建立 auto 组,200 高于 100,业务输出必须为 report=v2。重复注册相同 path 更新该候选的记录,不生成第二个同路径候选。此块末尾针对 auto/v2 状态验收;若此前手动选过 v1,先执行第 2.3 节第 2 步回到 auto,再重复本块,不能把“manual 未自动换版本”当作注册失败。
2.3、固定版本与恢复自动选择
1、明确选择 v1。两个从链接必须一起指向 v1,业务输出为 report=v1;重复执行保持 manual/v1。
bash <<'ALT_SET_MANUAL'
set -Eeuo pipefail
export LC_ALL=C
printf '[切换] drafts-report 固定 v1\n'
ROOT=/opt/alternatives-demo
ALT=$(command -v alternatives || command -v update-alternatives)
"$ALT" --set drafts-report "$ROOT/v1/report"
"$ALT" --display drafts-report
test "$(readlink -f "$ROOT/report.conf")" = "$ROOT/v1/report.conf"
test "$(readlink -f /usr/local/share/man/man1/drafts-report.1)" = "$ROOT/v1/report.1"
test "$(/usr/local/bin/drafts-report)" = report=v1
printf 'ALT_SET_MANUAL_OK\n'
ALT_SET_MANUAL
2、恢复 auto,重新按当前候选优先级选择 v2。重复执行保持 auto/v2。
bash <<'ALT_SET_AUTO'
set -Eeuo pipefail
export LC_ALL=C
printf '[切换] drafts-report 恢复自动选择\n'
ROOT=/opt/alternatives-demo
ALT=$(command -v alternatives || command -v update-alternatives)
"$ALT" --auto drafts-report
"$ALT" --display drafts-report
test "$(readlink -f "$ROOT/report.conf")" = "$ROOT/v2/report.conf"
test "$(readlink -f /usr/local/share/man/man1/drafts-report.1)" = "$ROOT/v2/report.1"
test "$(/usr/local/bin/drafts-report)" = report=v2
printf 'ALT_SET_AUTO_OK\n'
ALT_SET_AUTO
演练完成继续第 10.3 节,保留审计结果后回收。实际应用不能把示例输出当成业务验收,应执行该应用的真实读写、兼容性和健康检查。
异常处理:注册或切换后未出现预期业务输出
停止后续切换,保存
--display输出及主从链接的readlink结果。配置和程序不匹配时不要继续启动业务;回到已验证版本,或对这个本次新建示例按第 10.3 节回收。已有客户组不能通过整组删除或覆盖状态文件处理。
3、本质与要解决的问题
3.1、多实现与稳定入口
多个包提供同一接口时,实际文件各自保留,调用方使用通用名。链路例如 /usr/bin/java → /etc/alternatives/java → 已安装的实际程序。通用名和组名是不同参数,即使示例中同名,也不能交换位置。
Alternatives 不改变调用方的 PATH、alias、shell 函数或缓存;应用显式使用 /opt/jdk/bin/java 时也不会经过 /usr/bin/java。不能仅凭链接切换成功就断言所有应用已换版本。
3.2、使用边界
| 需求 | 合适入口与边界 |
|---|---|
| 多个可互换实现的系统默认入口 | 使用发行版已有链接组,或为自建应用创建独立组 |
| 用户自己的编辑器或开发版本 | EDITOR、VISUAL、项目环境或版本管理器;不必改系统默认 |
| 编译时选择工具链 | 使用构建系统及 CC、CXX 等配置 |
| Debian 单文件替换 | 先判断是否应使用 dpkg diversion,不能与 alternatives 混用同一路径 |
| systemd 配置文件 | Debian Policy 明确禁止用 alternatives 管理;使用 systemd 自身的配置机制 |
修改系统 /usr/bin/python3 |
不自建覆盖型链接组,避免破坏系统工具依赖 |
4、设计动机与约束
4.1、两层链接与状态库
中间链接让管理员选择集中在 /etc/alternatives。通用链接通常保持稳定,但修复、首次创建或删除时也会变化;“切换永远只改 /etc”过于绝对,状态库、日志和从链接同样可能被更新。普通切换不替换候选程序的文件内容。

图中执行链路由文件系统解析;工具只在管理操作时读取状态并维护链接,不驻留为运行时代理。程序只读取配置,手册由 man 按需读取。
4.2、主从链接的成立条件
链接组以 master/leader 为主链接,slave/follower 为从链接。每个候选应提供匹配的从文件,切换主候选时按该候选的记录更新从链接。Debian 对不存在的从文件会跳过创建相应链接;chkconfig 1.19.2 对已登记但不存在的从文件仍会创建断链。没有登记某个从路径时也可能撤销相应链接。因此必须同时检查链接解析与目标文件存在,不能自动借用其他候选的配置。
多个链接的更新不是跨文件或跨进程的业务事务。需要程序、配置和长驻进程一致时,安排维护窗口,并在切换后按应用机制重载或重启、复验业务。不能把 alternatives 当作零停机发布工具。
4.3、auto、manual 与优先级
| 动作 | auto | manual |
|---|---|---|
| 注册更高优先级候选 | 通常切到更高优先级 | 当前有效候选仍保留 |
| 删除未选中的候选 | 重算可用候选 | 保留当前有效选择 |
| 删除当前候选 | 转向其他候选,无候选则撤销组 | 不能继续固定已删除候选,可能切换并回到 auto |
--set name path |
改为 manual | 固定指定的已注册 path |
--auto name |
重算最高优先级 | 回到 auto 并重算 |
manual 固定的是候选路径选择,不冻结该路径内的软件包内容。升级同一路径、删除当前包或候选损坏仍会影响业务。整数优先级越大越优先;同分时不要依赖安装顺序,把不同业务优先级设成不同值。
4.4、交互选择存在实现差异
Debian 的 --config 菜单通常含编号 0 的 auto 项;选择它仍是 auto。chkconfig 的菜单以实际候选编号选择,选后设为 manual。因此“运行 --config 就一定是 manual”不是跨发行版规则。交付自动化使用 --set 或 --auto,交互人员必须阅读实际菜单,不照抄其他主机的编号。
4.5、软件包维护契约
Debian 的 postinst configure 注册候选,prerm/postrm remove 撤销候选;一般不在 upgrade、disappear 等其他动作中进行重选,以免丢失管理员选择。包升级后仍会进入 configure,不能把“不要在 upgrade 动作中调用”误读成“升级过程中永不更新注册”。具体依赖该包维护脚本;RPM 系统有自己的脚本约定,不能直接套用 Debian 阶段名。
候选卸载前撤销注册,保留业务可用的替代实现。包维护脚本不应未经管理员授权用 --set 强制改变其选择。
5、实现与机制
5.1、目录与消费者
| 产物 | 维护者、消费者及作用 |
|---|---|
| 通用入口 | 工具创建,shell 或应用执行;第 2 节为 /usr/local/bin/drafts-report |
/etc/alternatives/ 链接 |
工具维护,文件系统路径解析时使用;主从名称都必须唯一 |
| administrative directory | 工具维护候选、优先级、模式及主从路径;Debian 与 chkconfig 默认目录不同 |
| 候选程序、配置和手册 | 软件包或应用交付方安装维护;alternatives 只管理链接,不替它们备份、安装或验证 |
Debian /var/log/alternatives.log |
默认操作日志,用于审计;使用自定义状态目录时,日志仍需单独考虑 --log |
磁盘格式不是跨发行版接口。当前选择还涉及实际链接,不能假定只复制一个状态文件就完成恢复;不要手工编辑状态文件或把一套实现的数据库复制给另一套。
5.2、操作效果与失败处理
--install 注册或更新候选,--remove 只撤销指定候选,--remove-all 撤销整个组及其受管链接。后两者不卸载真实程序。组被撤销后,本次入口不再可用,调用方必须已停止使用。
--set 要求候选已注册,仅安装了文件不代表可选。退出码为 0 之后仍应检查模式、实际解析路径和业务结果。失败可能留下部分文件变化,尤其在磁盘满、权限或并发写入时;不以非零退出码推定“什么都没改”。
6、术语与概念
| 术语 | 含义 |
|---|---|
| generic link | 用户调用的通用路径,如 /usr/local/bin/drafts-report |
| alternative name | 组名,如 drafts-report;对应中间目录和状态库中的名称 |
| alternative path | 一个已安装候选的绝对路径 |
| link group | 一个主链接及随其选择变化的从链接集合 |
| altdir | 中间链接目录,默认 /etc/alternatives |
| admindir | 状态目录;更改它不会自动隔离通用入口或其他写入 |
7、发行版与路径差异
| 实测系统 | 实际提供包 | 默认命令与状态目录 |
|---|---|---|
| Rocky Linux 8.10 | chkconfig 1.19.2-1.el8 |
alternatives;/var/lib/alternatives |
| Rocky Linux 9.8 | alternatives 1.24-2.el9 |
同上 |
| Anolis OS 23.3 | alternatives 1.25-1.an23 |
同上 |
| openEuler 22.03 LTS-SP4 | chkconfig 1.21-1.oe2203sp4 |
同上 |
| Debian 13.5 | dpkg 1.22.22 |
update-alternatives;/var/lib/dpkg/alternatives |
| Ubuntu 22.04.5 LTS | dpkg 1.21.1ubuntu2.6 |
同上 |
六个平台均为 x86_64 原生系统,在独立 mount/PID namespace 和写时复制目录中执行。没有替换宿主软件包或真实业务链接。openSUSE 测试机连接被拒绝,SUSE 官方机制说明仅作来源核对,不列入本轮实测签收。RHEL、SLES、其他版本与架构均须单独验收。
Rocky 等主机上即使存在 update-alternatives 同名入口,也可能实际是 chkconfig 实现;判断以 --version、随包手册和包归属为准,不能按命令名字推断功能。
8、命令参考
8.1、共同语法
下表为语法说明,不是可直接粘贴的命令。link、path 使用绝对路径;不要把参数名当成实际文件。
| 操作 | 参数顺序 |
|---|---|
| 注册 | --install link name path priority [--slave slave-link slave-name slave-path] |
| 指定候选 | --set name path |
| 自动选择 | --auto name |
| 交互选择 | --config name |
| 详情 | --display name |
| 撤销一个候选 | --remove name path |
| 撤销整个组 | --remove-all name |
8.2、不同实现的查询
| 任务 | chkconfig alternatives | Debian update-alternatives |
|---|---|---|
| 所有组及选择 | --list |
--get-selections |
| 指定组所有候选 | --display name 中查看 |
--list name |
| 机器可读详情 | 不假定有 --query |
--query name |
| 导入模式与选择 | 不支持 Debian 选择文件接口 | --set-selections |
| 详细或安静输出 | 核对本机 --help,不假定有 --quiet |
--verbose、--quiet |
Debian 手册定义常规成功为 0、错误为 2;chkconfig 各错误路径也可能返回其他非零值。脚本按零/非零处理,不把所有发行版、所有失败固定为一个数字。--altdir、--admindir 不是完整沙箱:通用入口、候选和 Debian 日志路径也必须独立指定或隔离。
9、实践案例概要
自建应用可参照第 2 节建立专属主从组。软件包提供的 Java、编辑器和 MTA 应先沿用其已有组及发行版集成方式;不存在组时,停止套用切换命令。批量选择恢复只能恢复“已存在的组和候选的选择”,不能部署缺少的程序。
10、实践案例
10.1、Java 全族与既有软件包
1、在已有 JDK 的主机由管理员先只读检查:
bash <<'ALT_INSPECT_JAVA'
set -Eeuo pipefail
printf '[检查] Java、编译器及其实际链接组\n'
ALT=$(command -v alternatives || command -v update-alternatives)
command -v java
"$ALT" --display java
java -version
if command -v javac >/dev/null; then
"$ALT" --display javac
javac -version
fi
printf 'ALT_INSPECT_JAVA_OK\n'
ALT_INSPECT_JAVA
Java runtime 与编译器可能是不同 master 组。不能对已安装软件包直接重建 java 并把现有 javac master 改成 slave;候选与手册路径均以本机已安装包为准。缺少 Java 或独立 javac 组时本检查会停止,按包归属核对实际拓扑,不借此改组。
2、Debian 的 java-common 提供 update-java-alternatives --list,按 .jinfo 记录协调同一套 JRE/JDK;其 --set jname 是 Java 专属接口,不是 update-alternatives --set name path。先确认本机安装该工具、候选及全部依赖,再按该包手册维护整套 Java。RPM 系统分别检查现有 runtime、compiler 等组,逐组保存模式和原路径,再选择属于同一目标 JDK 的已注册候选;应用的 JAVA_HOME、systemd 环境与容器路径另行维护。
3、真实 JDK 升降级必须另验语言版本、类库、构建和应用启动,失败按各组原模式/路径及应用配置回退。本轮核心验收不代替 Java 软件包安装、整套 JDK 迁移或应用兼容验收。
10.2、MTA 与编辑器
mta 是否存在由发行版和已安装包决定。alternatives 不负责停止原邮件服务、迁移队列、配置 TLS、修正防火墙或启动新服务。没有实际邮件队列和发收测试时,不签收 MTA 迁移;不能只切 /usr/sbin/sendmail 就认定邮件切换成功。
编辑器同样先用本机 --display editor 判断该组是否存在。用户只需个人偏好时用 EDITOR/VISUAL;不为了演练新增或接管生产组。
10.3、自建组的卸载与回收
执行位置仍为第 2 节测试机 root 终端。下面仅处理本次创建的示例,删除其候选、状态、入口与示例制品。先将需要保存的输出归档到变更记录,确保没有使用该入口的进程;不要将此操作套用 Java、Python、MTA 或原有组。
回滚:第 2 节失败或独立示例演练完成
bash <<'ALT_CLEANUP' set -Eeuo pipefail printf '[回收] 仅删除本次 drafts-report 示例\n' test "$(id -u)" -eq 0 ROOT=/opt/alternatives-demo ALT=$(command -v alternatives || command -v update-alternatives) test ! -L "$ROOT" test "$(cat "$ROOT/OWNER")" = drafts-alternatives-demo-v1 if "$ALT" --display drafts-report >/dev/null 2>&1; then "$ALT" --remove-all drafts-report fi for path in /usr/local/bin/drafts-report /usr/local/share/man/man1/drafts-report.1 "$ROOT/report.conf"; do test ! -e "$path" test ! -L "$path" done for name in drafts-report drafts-report.conf drafts-report.1; do for directory in /etc/alternatives /var/lib/alternatives /var/lib/dpkg/alternatives; do test ! -e "$directory/$name" test ! -L "$directory/$name" done done rm -rf -- "$ROOT" test ! -e "$ROOT" printf 'ALT_CLEANUP_OK\n' ALT_CLEANUP初始化尚未创建 OWNER 就中止时,上述脚本会拒绝删除,管理员应先核对精确目录的归属和内容,不直接扩大删除范围。已回收后的再次执行也会因 OWNER 不存在而停止;先确认所有路径已不存在即可,不重新创建文件以绕过保护。若组查询失败但状态文件仍在,最终门禁会阻断删除制品,保留现场用于诊断。
10.4、Python 的无版本号入口
部分发行版允许包管理的 python 组,其他发行版未必有;已安装 /usr/bin/python3.11 也不代表它已在 python 组注册。先核对本机 --display python、包归属及应用 shebang。不存在该组就停止,不直接执行 --set python /usr/bin/python3.11。
项目应用优先使用明确版本解释器和虚拟环境。系统 /usr/bin/python3 不新增自定义 alternatives;SUSE 官方明确说明这会破坏依赖系统测试版本的工具。改变项目解释器还须验依赖和实际业务,不能只验 python --version。
10.5、Debian 的选择导出与恢复
此节仅适用于 Debian/Ubuntu 的 dpkg 实现。先完成第 2 节,再执行第 2.3 节第 1 步,使示例处于 manual/v1。保存在 /opt/alternatives-demo/selection.before 的文件由管理员创建,仅供本节 --set-selections 读取;它不是组定义或软件包备份。
1、导出这个示例组的原模式和选择,拒绝覆盖已有首次记录:
bash <<'ALT_EXPORT_SELECTION'
set -Eeuo pipefail
export LC_ALL=C
printf '[备份] 仅导出 drafts-report 的选择\n'
ROOT=/opt/alternatives-demo
test ! -e "$ROOT/selection.before"
test ! -L "$ROOT/selection.before"
update-alternatives --query drafts-report
update-alternatives --get-selections | awk '$1 == "drafts-report"' > "$ROOT/selection.before"
test "$(wc -l < "$ROOT/selection.before")" -eq 1
grep -Eq '^drafts-report[[:space:]]+manual[[:space:]]+/opt/alternatives-demo/v1/report$' "$ROOT/selection.before"
printf 'ALT_EXPORT_SELECTION_OK\n'
ALT_EXPORT_SELECTION
2、执行第 2.3 节第 2 步切到 auto/v2,再恢复:
bash <<'ALT_RESTORE_SELECTION'
set -Eeuo pipefail
export LC_ALL=C
printf '[恢复] drafts-report 原模式和选择\n'
ROOT=/opt/alternatives-demo
test -x "$ROOT/v1/report"
grep -Eq '^drafts-report[[:space:]]+manual[[:space:]]+/opt/alternatives-demo/v1/report$' "$ROOT/selection.before"
update-alternatives --set-selections < "$ROOT/selection.before"
query=$(update-alternatives --query drafts-report)
printf '%s\n' "$query"
grep -Fx 'Status: manual' <<< "$query"
grep -Fx "Value: $ROOT/v1/report" <<< "$query"
test "$(/usr/local/bin/drafts-report)" = report=v1
printf 'ALT_RESTORE_SELECTION_OK\n'
ALT_RESTORE_SELECTION
恢复可重复执行。跨机器恢复前,必须先安装对应软件包、注册相同组和目标 path,并核对架构及主从拓扑;缺少组或候选时,导入可能报告警告而未完成目标恢复,不能只看退出码。不要把整机 --get-selections 文件无审查导入另一台机器,也不要用简单按空格切字段的方式处理任意带空格路径。
异常处理:选择恢复未达到原模式或原路径
保留选择文件和查询输出,停止后续业务切换。先恢复所需候选制品和注册关系,再针对该组恢复模式及选择,并检查主从文件与业务结果。没有候选定义、软件包和配置备份时,选择文件本身不能提供灾难恢复。
10.6、删除当前候选与升级回退
删除前先用 --display 保存当前候选、模式及全部从链接。删除未选中候选与删除当前候选的后果不同:后者在 manual 下也可能回 auto,不能用“已固定”作为卸载安全保证。自建应用优先先 --set 到经过验证的保留版本,复验后 --remove 旧 path,最后才删除旧制品。先保留旧版本、配置及业务恢复手段,再升级;新版本不通过时用旧候选恢复业务。
11、排错与常见问题
1、先查实际调用入口。Bash 中使用 type -a drafts-report、command -v drafts-report、readlink -f /usr/local/bin/drafts-report;PATH 前置目录、alias 和函数可能绕过受管链接。变更 PATH 或命令位置后可用 hash -r 清除当前 Bash 的命令位置缓存。
2、用本机 --display 看模式、当前链接、候选和优先级,再逐个检查从链接、文件权限和业务输出。不要只看“best”就认定它是 manual 下的当前选择。
3、断链时先区分候选文件缺失与中间链接缺失,保存状态和包归属,核对权限及文件系统。候选文件缺失先恢复对应软件包或制品;中间链接缺失按以下分支处理,不能假定 --auto 总能重建链接。
异常处理:
/etc/alternatives中的主链接缺失Debian 的 dpkg 实现可用目标组的
--auto重建链接,但会恢复 auto 模式。chkconfig 1.19.2 的readConfig会因readlink失败而返回 2,--auto和--set均在写入前停止;--install的注册入口允许继续修复。这是实现差异,不使用手工ln -sf绕过状态库。对第 2 节的本次示例,确认两个候选及从文件完整后,重新执行第 2.2 节的完整注册块,再执行第 2.3 节第 2 步,验收 auto/v2、两个从链接及
report=v2。恢复 manual/v1 时随后执行第 2.3 节第 1 步。已有业务组必须依据所属软件包或原始交付记录恢复完整的--install参数,再按变更前记录恢复模式与路径并复验业务;参数、状态文件或归属不明时停止,保留现场,不撤销整组。异常处理:普通文件占用了通用入口、状态损坏或并发变更
停止操作并保存精确文件、链接、状态和包归属;不要用
--force、ln -sf或直接编辑数据库抢占路径。先协调其他维护者,恢复包管理或已有应用认可的布局,再对单组重新验收。主从切换不提供全业务事务,也不替代变更协调。
12、运维与安全要求
候选目录及其父目录不能允许非授权用户写入,否则系统入口会执行被替换的程序。第 2 节使用 root 维护的 /opt、/usr/local,不放置密码或密钥。实际配置如含凭据,应按应用权限模型独立管理,不能照抄示例配置的 0644。
保留模式、路径、优先级、主从拓扑、制品版本/摘要及变更前后的业务结果。备份与恢复应包含真实程序、配置、包来源和重新注册流程;跨发行版不直接复制状态库。manual 不等于包版本锁定,软件包版本策略仍由包管理和变更流程实施。
13、命令速查
| 任务 | 示例组的操作入口 |
|---|---|
| 完整注册与 auto 验收 | 第 2.2 节脚本 |
| 固定 v1 并验业务 | 第 2.3 节第 1 步 |
| 恢复自动选择并验业务 | 第 2.3 节第 2 步 |
| 交互选择 | 本机工具的 --config drafts-report;交互脚本需从 /dev/tty 读取 |
| 单候选卸载 | 先切换到保留版本,再 --remove drafts-report 已注册的绝对候选路径;此行为先在隔离环境验证 |
| 删除本次示例 | 第 10.3 节专属回收脚本 |
| Debian 选择恢复 | 第 10.5 节;不等于软件或组定义恢复 |
14、常见误区与纠正
| 错误判断 | 实际边界 |
|---|---|
| manual 下删除当前实现也不会变 | 当前实现不存在时可能切换并回 auto |
--config 一定进入 manual |
Debian 可选 auto 项,菜单依实现而异 |
--list 在所有系统都表示列出所有组 |
chkconfig 无 name,Debian 需要 name 且列候选 |
| 调整 java 会自动调整全部 JDK 工具 | 取决于原组关系;独立 master 不会自动联动 |
| 两个候选从文件可以不完整 | 缺失从文件可能使对应链接缺失或形成断链,必须检查真实文件与业务 |
| 导出选择就是完整备份 | 选择文件不包含注册、二进制、优先级、主从拓扑和应用数据 |
| 改完链接,运行中的服务已换版本 | 已启动进程需按应用机制处理并重新验业务 |
15、官方依据
本次按发行版已安装的稳定包验证,不单独升级系统 alternatives。官方源码用于核对实现差异,实际版本及源码摘要另见验收记录。
- Debian trixie:update-alternatives(1):模式、删除、查询及选择导入;本次页面对应 dpkg 1.22.22。
- Debian Policy:Alternative versions of an interface:软件包协作与 systemd 配置边界;本次页面版本 4.7.4.1。
- chkconfig 1.24 官方源码:
configService、removeService、主从参数兼容及模式变化。 - chkconfig 1.19.2 官方源码:Rocky 8 断链时
readConfig、autoService、setService与addService的不同处理。 - dpkg 1.22.22 官方维护者源码:Debian 13 对应版本的选择与注册实现。
- dpkg 1.21.1 官方维护者源码:Ubuntu 22.04 基线的选择导入、缺失组处理和日志路径。
- Debian trixie:update-java-alternatives(8):通过
.jinfo协调 JDK 工具。 - SLES 15 SP7:Managing multiple versions:主从组与禁止自建 python3 alternatives;不代表已完成 SLES 实测。
