这台 Debian 12 (bookworm) 上原本想装个 tmux,结果一路排下去发现系统被手动塞进了一批 testing/trixie 的新版本包(libtinfo6、ncurses、systemd、openssh…),跟 bookworm 的依赖矩阵彻底对不上,最后连 python3、openssh-server、cmake 都被拖下水卸掉了。
这篇记录完整的排查与修复过程:怎么把整批”跨版本包”一次性降回原生版本、再把被卸掉的关键包装回来,全程不留坑。
目录
一、起因:装 tmux 直接报”破坏依赖”
一开始只是想在 bookworm 上装个终端复用工具:
$ apt-get install tmux ... 下列软件包有未满足的依赖关系: libtinfo6 : 破坏: tmux (< 3.3a-4) 但是 3.3a-3 正要被安装 E: 无法修正错误,因为您要求某些软件包保持现状,就是它们破坏了软件包间的依赖关系。
这条报错看着像 Debian unstable 常见的”状态无法满足”,但 bookworm 是稳定版,不该出这种提示。往下查才发现不对劲。
二、定位:libtinfo6 是从”未来”装来的
先查 libtinfo6 当前装的是哪个版本:
$ dpkg -l libtinfo6 && apt-cache policy libtinfo6
ii libtinfo6:amd64 6.6+20251231-1+b1 amd64 shared low-level terminfo library
libtinfo6:
已安装:6.6+20251231-1+b1
候选: 6.6+20251231-1+b1
版本列表:
*** 6.6+20251231-1+b1 100
100 /var/lib/dpkg/status
6.4-4 500
500 http://deb.debian.org/debian bookworm/main amd64 Packages
两条关键信息:
- 版本号
6.6+20251231-1+b1里的日期戳是 2025-12-31,这显然是 testing/trixie 时代的构建,不属于 bookworm。 - 它的优先级是
100 /var/lib/dpkg/status——也就是本地 dpkg 状态里登记的手动包,而不是从源候选进来的。说明之前有人(或某次操作)手动dpkg -i或从临时源把它拉了进来。
矛盾点:bookworm 自带的 tmux 3.3a-3 明确写了 Breaks: libtinfo6 (>= 新版),只能配旧版 6.4;系统里却被顶成了 6.6。两边对不上。
三、真凶:系统里混了一整批跨版本包
试着单独把 libtinfo6 降回 bookworm 的 6.4-4,又报 ncurses-bin 要求 libtinfo6 >= 6.6,形成循环依赖。再想降级,apt 提示要连带卸载 45 个包(含 python3、openssh-server、cmake、google-chrome、libncursesw6 等),根本不能这么干。
把”非 bookworm”的包全揪出来(dpkg -l 里带 bpo 或异常日期戳的):
$ dpkg -l | grep -Ei 'bpo|2025|2024|~rc' ii e2fsprogs 1.47.2-3~bpo12+1 amd64 ... ii iproute2 6.15.0-1~bpo12+1 amd64 ... ii libbpf1 1:1.4.6-1~bpo12+1 amd64 ... ii libelf1 0.192-4~bpo12+1 amd64 ... ii libsystemd-shared 254.26-1~bpo12+1 amd64 ... ii libsystemd0 254.26-1~bpo12+1 amd64 ... ii libudev1 254.26-1~bpo12+1 amd64 ... ii openssh-client 1:10.0p1-7~bpo12+1 amd64 ... rc openssh-server 1:10.0p1-7~bpo12+1 amd64 ... ii openssh-sftp-server 1:10.0p1-7~bpo12+1 amd64 ... ii systemd 254.26-1~bpo12+1 amd64 ... ii systemd-sysv 254.26-1~bpo12+1 amd64 ... ii udev 254.26-1~bpo12+1 amd64 ... # 另外还有 coreutils 9.10-1、libncursesw6 6.6 等,比 bookworm 更新
rc 状态:openssh-server 是 rc(已卸但配置文件保留),说明之前被删过一次又试图装回来——这也是依赖混乱的佐证。结论:得把这整批包一次性降回 bookworm 原生版本,依赖链才能对齐。
四、抢救原则:成组降回 bookworm 原生版本
4.1 先查每个包在 bookworm 源里的精确版本号,别凭记忆
$ apt-cache policy coreutils e2fsprogs iproute2 libbpf1 libelf1 \
libext2fs2 openssh-client openssh-server systemd
# 每个包会列出「已安装 / 候选 / 版本列表」,抄 bookworm/main 源里
# 优先级 500 那一行的版本号(带 deb12 / 纯 bookworm 构建号)
4.2 一条命令成组降级(–allow-downgrades 必带)
$ apt-get install -y --allow-downgrades \
coreutils=9.1-1 \
e2fsprogs=1.47.0-2+b2 \
libext2fs2=1.47.0-2+b2 \
iproute2=6.1.0-3 \
libbpf1=1:1.1.2-0+deb12u1 \
libelf1=0.188-2.1 \
openssh-client=1:9.2p1-2+deb12u10 \
openssh-sftp-server=1:9.2p1-2+deb12u10 \
openssh-server=1:9.2p1-2+deb12u10 \
systemd=252.39-1~deb12u2 \
systemd-sysv=252.39-1~deb12u2 \
libsystemd-shared=252.39-1~deb12u2 \
libsystemd0=252.39-1~deb12u2 \
libudev1=252.39-1~deb12u2 \
udev=252.39-1~deb12u2
几个要点:
--allow-downgrades必须带,否则 apt 拒绝降级。- 配套包要一起写全:
e2fsprogs和libext2fs2是一对,只降一个会卡在「预依赖 libext2fs2 (= 1.47.0-2+b2) 但 1.47.2 正要被安装」。 - openssh 的 client / server / sftp-server 三个要降到同一个
9.2p1-2+deb12u10,版本不一致又会咬上。 coreutils这种核心工具降级前先确认有别的救援手段(云控制台 VNC / 其他环境),因为ls/cp/mv/rm会被替换。
apt-get -s install 看会不会连带卸载大量关键包,确认安全再真正执行。五、收尾验证:SSH / tmux / Python 全恢复
降级完成后,被误卸的关键包要装回来。先看 Python 是不是也被卸了(当时那 45 个被卸载的包里就有 python3.11-venv):
$ dpkg -l | grep -E 'python3|venv' ii libpython3.11-minimal:amd64 3.11.2-6+deb12u9 amd64 ... rc python3 3.11.2-1+b1 amd64 ... ii python3-minimal 3.11.2-1+b1 amd64 ... rc python3.11-venv 3.11.2-6+deb12u9 amd64 ... # 注意:python3 和 python3.11-venv 都是 rc(已卸载),venv 模块因此缺失
这就是为什么后来 python3 -m venv 会报 No module named venv。装回来:
$ apt-get install -y python3 python3.11-venv # 会新装 7 个包:python3 / python3.11 / python3.11-venv / # libpython3-stdlib / libpython3.11-stdlib / python3-distutils / python3-lib2to3
最终验证:
$ systemctl restart ssh && systemctl status ssh | head -3
● ssh.service - OpenBSD Secure Shell server
Active: active (running) ...
$ tmux -V
tmux 3.3a
$ python3 --version
Python 3.11.2
$ python3 -m venv venv
# 成功,不再报 No module named venv
apt-get check 确认没有未解决的依赖;再 systemctl --failed 看有没有挂掉的服务。六、教训与铁律
这次崩盘的根本不是 tmux 或 python 本身,而是系统里被手动塞进了一批跨版本(testing/trixie 构建)的包,它们与 bookworm 的依赖矩阵对不上,连锁反应把一批基础包拖下水。
三条铁律:
- 别跨版本手动塞包:同一台机器上不要一会儿 bookworm、一会儿从 testing/trixie 拉包,混装必炸。临时需要新特性,用
docker/virtualenv/pyenv隔离,别往系统里塞。 - 动手前先
apt-cache policy <pkg>查可用版本,按源里真实存在的精确版本号写,别凭记忆。 - 降级要成组:把”被污染的整批包”列全(
dpkg -l | grep -Ei 'bpo|20[0-9]{4}'),一条命令统一降回原生版本,配--allow-downgrades,并保证配套包(server/client、e2fsprogs/libext2fs2)版本一致。
七、快速排查命令速查表
下次再遇到”装了个包却报破坏依赖 / 依赖对不上”,按这张表从上往下查,基本能定位是不是混版本包。
1. 先看依赖冲突卡在哪
# 模拟安装,不真装,看牵连哪些包 / 报什么冲突 apt-get -s install tmux # 查某包的依赖关系(谁需要它、它需要什么、谁破坏它) apt-cache depends tmux apt-cache showpkg tmux
2. 揪出”非本版本”的包(关键)
# 列出所有带 bpo / 异常日期戳 / rc 的包
dpkg -l | grep -Ei 'bpo|2025|2024|~rc'
# 或找版本号里带「未来日期」或「rc」的
dpkg -l | awk '{print $1,$2,$3}' | grep -E '20[0-9]{4}|rc'
# 反查某包在源里到底有哪些版本(哪行是原生 500 优先级)
apt-cache policy libtinfo6
3. 确认某包”哪来的”
# 安装/移除历史(是不是手动 dpkg -i 或临时源拉的) grep 'libtinfo6' /var/log/dpkg.log | tail -20 # 本地是否残留手动装的 deb find /var/cache/apt/archives/ /tmp /root -name 'libtinfo*.deb' 2>/dev/null
4. 成组降级前,把可用版本号抄齐
# 一次查全,抄「bookworm/main(优先级 500)」那行的版本号
apt-cache policy coreutils e2fsprogs iproute2 libbpf1 libelf1 \
libext2fs2 openssh-client openssh-server systemd
5. 一条命令成组降回原生版本
# 用第 4 步抄到的版本号拼成一条;--allow-downgrades 必带
apt-get install -y --allow-downgrades \
coreutils=9.1-1 \
e2fsprogs=1.47.0-2+b2 \
libext2fs2=1.47.0-2+b2 \
openssh-client=1:9.2p1-2+deb12u10 \
openssh-server=1:9.2p1-2+deb12u10 \
openssh-sftp-server=1:9.2p1-2+deb12u10 \
systemd=252.39-1~deb12u2
# 配套包务必同一批、版本一致
6. 收尾验证
# 依赖是否还有未解决的 apt-get check # 关键服务 / 工具能否正常跑 systemctl restart ssh && systemctl status ssh | head -3 tmux -V python3 --version && python3 -m venv venv
排查口诀(六步法)
apt-get -s install模拟看冲突dpkg -l | grep -Ei 'bpo|20[0-9]{4}'揪混版本包apt-cache policy抄原生版本号apt-get install --allow-downgrades成组降回- 装回被误卸的关键包(python3 / openssh-server 等)
apt-get check+ 重启服务验证
全程别跨版本手动塞包,需要新特性就用 docker / virtualenv 隔离。
ChiuYut
2026年10月25日