我第一次把 Linux 当主力系统用的时候,第一个让我想砸键盘的环节,不是编辑配置文件,而是装软件。在 Windows 上装个工具,下载 exe、双击、下一步、完成,基本是直觉操作。到了 Linux 下,光"安装软件"就能牵扯出 apt、dnf、pacman、zypper、dpkg、rpm、snap、flatpak、AppImage 这一大串名字,问身边的大神,他只会说一句"自己查文档"。后来我花了不少时间把 Linux 下的软件管理完整捋了一遍,才明白这些五花八门的命令背后其实是同一套逻辑:软件包、依赖、软件源、签名校验。搞懂其中任何一套,换到别的发行版上只是换命令名的问题。
这篇文章就是我这些年实际使用中的理解汇总,包括各主流发行版包管理器的异同、换源的实操步骤、依赖出问题后的完整排查链路,以及什么时候该用源码编译、什么时候交给沙箱式打包。不管你现在用哪个发行版,不管你是完全没碰过 Linux 的新手,还是命令会一点但经常装完就坏的老油条,按这条思路走一遍,至少能把"装软件"这件事从玄学变成可预期的工程操作。
1. 软件包是什么:把"装软件"从玄学变回工程问题
1.1 一个安装包背后藏着四类信息
Linux 下的二进制软件包(deb、rpm,以及 Arch 里的 pkg.tar 产物)本质上是一个结构化归档。它和 Windows 那种自解压的 exe 最大的差别,不是格式,而是里面多了一层"元数据"。以 deb 包为例,用dpkg-deb -c能看到包里的文件清单,用dpkg-deb -I能读它的 control 段,里面写着包名、版本、维护者、依赖关系(Depends)、冲突关系(Conflicts)、替换关系(Replaces)。这层元数据,就是包管理器能做自动依赖处理的底气。
理解了这一点,很多新手问题就有答案了。有人问"为什么我手动下载一个 deb 文件,装了之后提示缺库,而用 apt 装同样的软件就顺顺当当?"因为 apt 在安装前会用索引解析依赖,再自动把缺的库一起拉下来,而 dpkg 只管解包和落盘。再比如"为什么系统老是让我运行 apt --fix-broken install?"也是因为当前要装的某个包,它的依赖和现有系统无法满足,需要先修掉破损状态再继续。所以别把软件包当"安装程序",把它当"一份自带说明书和购物清单的压缩包",后续的一切排障都会好理解得多。
1.2 包管理器的核心流水线:索引、依赖解析、下载、落盘
无论哪个发行版,包管理器做的事都是同一套流水线。第一步是读取本地维护的软件源索引,找到候选版本;第二步是根据包里的依赖元数据构建依赖树,找出还缺什么;第三步是从软件源下载所有需要的包并校验签名;第四步是解包到系统目录、执行安装脚本、更新数据库。你敲一个 install 命令,背后就跑完这四步。
所以读报错信息比背命令重要得多。比如 apt 报"Unable to locate package",大概率是索引里没有这个名字,要么包名写错,要么这个源里根本没收录;报"Package has no installation candidate",通常是收录了但当前系统版本不匹配,或者源里被剔除;报"Sub-process /usr/bin/dpkg returned an error code",则是第四步执行某个包的具体安装脚本时挂了,和索引解析无关。先定位是哪一步挂的,再去搜报错,比无头苍蝇一样重装系统效率高多了。
2. 三个主流分支的包管理器:命令不同,逻辑同源
2.1 Debian 系:apt 在前台,dpkg 在后台
Debian/Ubuntu 家族里,dpkg 是最底层的处理工具,直接面对 .deb 文件:dpkg -i安装本地包、dpkg -r卸载、dpkg -l列出已装包。但 dpkg 不会主动解决依赖,你给它一个缺库的包,它就报错放那。apt 是站在它上面的前端,包含一套完整的依赖解析、源管理、版本锁定和卸载清理策略。日常交互我几乎只用 apt,只有拿到官方网站下发的 .deb 文件时,才手动dpkg -i或者直接用apt install ./xxx.deb。
常用组合其实不多:sudo apt update刷新索引;apt search 关键词搜索;sudo apt install 包名安装;sudo apt remove/purge 包名卸载,remove 保留配置文件,purge 连配置一起删;sudo apt autoremove --purge清理不再需要的孤立依赖;sudo apt --fix-broken install修复破损依赖。至于 apt 和 apt-get 的区别,简单说 apt 是把常用子命令重新整合后的现代前端,命令更人性化,历史包袱少一些;写脚本时我会用 apt-get,因为它的输出和行为更稳定。
我在某台测试机上曾有一段"黑历史":贪快从网上抓 .deb 包,全部 dpkg -i 硬上,结果装一个包缺两个库,再装库又和另一个包冲突,系统进入半破碎状态。后来我把所有手工拉的包全部 purge,改用 apt 重装,才彻底缓过来。现在我的原则很简单:本地 .deb 只用来装那种没有进任何源、且官方只提供 deb 下载的软件,并且装完后立刻用 apt 的源管理手段把它纳入可追踪状态。
2.2 Red Hat 系:yum 已成符号,dnf 掌舵,rpm 打底
RHEL/CentOS/Fedora 系的历史有点绕。早年是 yum,底层用 rpm 处理包,yum 负责依赖解析和源管理。后来 yum 因为 Python2 的技术债,被用 Rust 重写出的 dnf 替代,并且给 yum 做了兼容符号链接。所以到了较新系统上,你敲 yum 和 dnf,实际基本是同一个东西。这套命令的思路和 apt 完全一致:dnf search 关键词、sudo dnf install 包名、sudo dnf remove 包名、sudo dnf autoremove。
这里要提醒一个点:rpm -Uvh ./xxx.rpm这种"直接操作 rpm"的方式不会解析依赖,等价于 dpkg -i。但dnf install ./xxx.rpm会连依赖一起处理。很多早年玩 CentOS 的习惯是 rpm -ivh 硬上,遇到依赖就一个个手动去网上找,属于远古时期的肌肉记忆。现在用 dnf 处理本地 rpm 包才是安全路径。另外dnf history可以查看安装历史并回滚到之前的事务,这个功能 apt 没有直接的等价物,出问题时非常好用。我每次在操作失败但不想手修依赖时,第一反应就是 dnf history 看看能不能 rollback。
2.3 Arch 系:pacman 快,但滚动更新有代价
Arch 的 pacman 命令极简:pacman -S 包名安装,pacman -Ss 关键词搜索,pacman -R 包名卸载,pacman -U 文件安装本地包。它的快,一部分来自不预设太多交互,另一部分来自仓库直接和上游同步得快。但 Arch 和 Debian 系维护哲学完全不同,它没有大版本冻结的概念,整个系统是滚动更新的。
所以对 Arch 用户来说,最危险的命令不是未知命令,而是sudo pacman -Sy之后立刻装一个包。你只更新了数据库索引,系统还停在旧的全库快照,新装的包很可能是按新版本库构建的,这就容易出现老玩家常说的"部分升级"(partial upgrade)问题,严重时甚至会让系统进入依赖不一致的状态。我的习惯是:所有 Arch 机器,装任何软件前先执行sudo pacman -Syu,把整个系统升级到当前索引一致的状态,再装目标包。这样看起来多花了时间,实际上能避开大量依赖打架的问题。另外 pacman 的下载缓存存放在 /var/cache/pacman/pkg,会越积越大,我每次更新完都会sudo pacman -Scc清一遍。
2.4 一张跨发行版的速查表
| 操作 | Debian/Ubuntu | RHEL/Fedora | Arch |
|---|---|---|---|
| 刷新索引 | sudo apt update | sudo dnf makecache | sudo pacman -Sy |
| 搜索软件 | apt search 关键词 | dnf search 关键词 | pacman -Ss 关键词 |
| 安装 | sudo apt install 包名 | sudo dnf install 包名 | sudo pacman -S 包名 |
| 卸载(含配置) | sudo apt purge 包名 | sudo dnf remove 包名 | sudo pacman -Rns 包名 |
| 更新全部 | sudo apt upgrade | sudo dnf upgrade | sudo pacman -Syu |
| 修复依赖 | sudo apt --fix-broken install | sudo dnf distro-sync | sudo pacman -Dk |
| 安装本地包 | sudo apt install ./x.deb | sudo dnf install ./x.rpm | sudo pacman -U x.pkg.tar.zst |
这张表不用死记,重点记每个家系的"底层包工具 + 前端解析工具"配对关系:dpkg+rpm 是底层,apt/dnf/pacman 是负责互动和依赖的壳。换发行版时,只要把命令名对上去,逻辑几乎可以平移。
3. 软件源:最常见的速度瓶颈和最容易被忽略的安全边界
3.1 软件源就是"包的进货渠道",换源本质是换镜像
包管理器默认会预先配置官方软件源,源是一个 URL 列表,指向存放包文件的仓库。你执行apt update或dnf makecache,就是把远端仓库索引拉到本地,生成一份"我能装什么、从哪下载"的清单。所谓换源,就是把 URL 改成地理位置更近、带宽更好的镜像站,让下载变快。注意,换源换的是"购买的渠道",不是"商品的产地",软件包本身还是上游构建的同一份,密钥校验规则没有变。
为什么同样是装一个包,换源前后速度差距巨大?原因就是网络链路:默认官方源服务器可能距离远、路径拥塞,而镜像站同步了一份仓库快照,访问快很多。但换源不是越多越好,一个发行版同时挂七八个第三方源,等于自己给自己制造依赖冲突。我见过有人的系统同时开了三个 Ubuntu PPA、两个 Debian 镜像、一个 Snap 商店,结果装任意一个稍大点的包都走一遍"版本打架"流程。源的数量应该控制在"够用"两个字上,而不是"越多越快"。
3.2 Debian 系换源实操:从 sources.list 到 deb822 新格式
Debian 系的源主要写在 /etc/apt/sources.list,新版系统也可能用 deb822 格式放在 /etc/apt/sources.list.d/ 下的 .sources 文件里。老格式一行一个 source,举个例子:
deb http://deb.debian.org/debian bookworm main contrib non-free其中 deb 表示二进制包,后面是基础 URL,bookworm 是发行版代号,main 是官方维护的自由软件包,contrib 是依赖闭源组件的自由软件,non-free 是闭源软件。Ubuntu 则类似,只是分区名字换成了 main universe restricted multiverse。换源最简单可靠的做法是:先备份原文件,再把 URL 部分替换成你选定的镜像站地址,改完执行sudo apt update。
新版 Debian 的 deb822 文件字段更结构化,会看到 URIs、Suites、Components 等字段,换的时候只改 URIs、保留 Suites 和 Components 即可。这里有一个我在实际维护中踩过的小坑:只把第一行的地址改了,后面的服务器路径没同步,导致 apt update 报了乱七八糟的"源不匹配"错误。后来我学乖了,每次换源后先apt update,如果输出里的序号和来源地址一致,再继续装软件。另外提醒一句,改完源之后如果直接装软件,本地索引里存的还是旧源信息,速度不会有任何变化,必须先更新索引。
3.3 Red Hat 系的 repo 文件与扩展源
RHEL/Fedora 系的源由 /etc/yum.repos.d/ 下的 .repo 文件管理。每个文件定义一组仓库,常见字段有 name(显示名)、baseurl(基础地址)、gpgcheck=1(启用 GPG 校验)、gpgkey(密钥位置)。换源时需要把 baseurl 改成镜像站地址,同时注释掉 metalink 这类自动解析行,因为 metalink 会把请求重定向到官方选定的服务器,导致你改了地址却仍然走慢链路。
Fedora 系经常需要添加扩展源,最常见的就是 EPEL(Extra Packages for Enterprise Linux)。它和 BaseOS 存储库之间偶尔会有包冲突,比如某些依赖版本不一致,导致 dnf update 一直刷版本来回跳动。碰到这种情况,第一件事不是去禁 EPEL,而是先dnf repolist --enabled看看启用了哪些源,再用dnf repository-packages查看某个源里具体提供哪些包。很多时候问题出在同一个包同时出现在官方源和扩展源,这时用 dnf 的--disablerepo参数临时关掉某个源再操作,比直接删配置文件干净得多。
3.4 换源之后必须保留的底线:GPG 签名与密钥
源可以换,但 GPG 校验不能关。包管理器下载包后会验证数字签名,确保这个包确实由维护该源的密钥签名。如果检测到包被篡改或者密钥不匹配,会直接拒绝安装。这就是为什么换源后经常遇到 NO_PUBKEY 报错:本地没有该源的公钥。正确做法是从源官网下载 keyring 文件放到 /usr/share/keyrings/,再在源配置里用signed-by=引用它。
我见过有人贪图省事把 gpgcheck 改成 0,或者在 apt 源配置里加allow_unsigned=1,这是把整个包管理器的信任链直接废掉。你装上什么,完全取决于镜像站想给你什么。省一点网络时间换这种风险,不值得。还有一点,老的apt-key adv --recv-keys命令虽然方便,但新版 Debian/Ubuntu 已经把它标为弃用,因为它会把第三方密钥塞进系统全局信任区。后面排障章节我会再详细讲一次我在这个问题上栽过的跟头。
4. 源码编译安装:何时必须返璞归真,以及怎么控制它
4.1 三种必须编译的场景
包管理器不是万能的,源码编译仍然有存在价值,但必须承认它属于"例外路径"而不是"默认路径"。最常见的三种场景是:官方仓库版本太旧,某个软件新版本已经发布,但发行版仓库还停在两三年前;你需要自定义编译参数,比如编译 Nginx 时想带某些特定模块进去,预编译包不可能照顾到每个人的个性化组合;再一种就是项目官方明确只提供源码包,根本没为你的发行版打包。
但是很多人把编译安装当成"装不上就编译"的万能后备,这其实是误解。编译安装意味着包的安装路径和升级都脱离了包管理器,你装的是"你自己维护的一个软件实例"。如果源码版本和系统库里某个库的版本不匹配,编译时会失败;如果系统库升级,编译装的软件又可能因为 ABI 变化直接挂掉。所以我的第一原则是:能进仓库绝不用源码,非用不可时才用,而且要用得尽量可控。光有"装上去"是不够的,还要想清楚"以后怎么升级、怎么卸载",否则三个月后你根本想不起当时到底把它装在哪了。
4.2 从 configure 到 make install 的三个关键步骤
标准三步是./configure && make && sudo make install。configure 本质是一个 shell 脚本,它会探测系统环境:有没有编译器、关键库是否存在、某些特性能不能编译,然后生成 Makefile。绝大多数来自 autotools 体系的项目都是这个流程。第一步建议先装好基础工具链:Debian 系执行sudo apt install build-essential pkg-config,Red Hat 系执行sudo dnf group install "Development Tools"。如果项目依赖某个库,一般还要装它的开发版包,比如 libfoo-dev,名字里带 -dev 或 -devel 的就是这类包。
configure 失败通常只有两种原因:找不到编译器,或找不到某个库。报错信息里会有checking for libfoo... no这种输出,根据这个 no 去补对应的开发包即可,不用把整个输出看一遍。第二步make -j$(nproc)用全部核心并行编译,能节省大量时间。编译过程如果报错,多半是源码本身与当前库版本不兼容,或者内存不足,用dmesg查一下有没有 OOM 也能确认。第三步make install默认装到 /usr/local,这其实是最合理的位置,因为 /usr/local 是给本地管理员手动装的软件用的,和发行版包管理器管理的 /usr 体系天然隔离,系统包升级时不容易互相覆盖。
--prefix参数值得单独讲一讲。configure 最常见的选项是--prefix=/usr/local,想装到家目录也可以--prefix=$HOME/.local,这样甚至可以不用 sudo。我自己编译小众工具时,会更激进地使用独立目录,比如--prefix=/opt/工具名,最终产物就是 /opt/工具名/bin、/opt/工具名/lib,卸载时rm -rf /opt/工具名完事。这个方法看起来土,实际上维护成本极低。
4.3 编译安装的管理策略:独立目录、打成本地包、善用 uninstall
源码安装的最大痛点不是编译过程,而是卸载和升级。有些项目的 Makefile 实现了make uninstall,如果你的 prefix 是 /usr/local,它能按安装清单删掉已安装文件;但很多项目根本没实现这个目标,装完之后想在 /usr/local/bin 里逐个找文件删,又累又不安全。所以安装位置的选择,直接决定了以后维护的负担。
另一种思路是装完立刻打回本地包:Debian 系用 checkinstall 工具可以把make install变成一个 .deb 包,之后dpkg -r就能卸载,apt 也能看到它。但要注意,这个包里如果包含的文件名和系统里已有包冲突,仍然会互相覆盖,并没有什么魔法。我个人的建议是,优先用--prefix指定独立目录,避免软件散落在 /usr/local/bin、/usr/lib 各处。如果这个工具确实常用,就写一个 systemd unit 文件或者 .desktop 启动器指向这个路径。这样编译装的软件虽然"游离"在包管理系统之外,但位置和生命周期都在你掌控范围内。
5. 依赖冲突与常见故障:一条完整的排查链路
5.1 broken packages 的根因与修复顺序
依赖冲突最常见的表现就是"装 A,提示要修依赖;修依赖,提示要把某个无关紧要的包一起拉进来;拒绝,然后什么都装不了"。这种烂摊子的根因通常有三个:本地 dpkg 数据库记录的状态和实际文件不一致、第三方源的包和官方源版本互相冲突、或者上一次安装中断留下半配置状态。
我的处理顺序是固定的。先跑sudo dpkg --audit,看有没有包处于未完成状态;再跑sudo apt --fix-broken install,让 apt 自动尝试修复;接着apt list --broken列出破损包;如果还不行,就根据报错里的具体包名,手动apt install 包名=版本号指定到可满足依赖的版本,或者直接apt purge掉肇事包。如果 dpkg 提示某些包处于 iU(半安装)或 iF(半配置)状态,说明配置脚本没跑完,可以sudo dpkg --configure -a重跑一遍配置。
我见过很多人在这一步直接rm -rf /var/lib/dpkg,等于把数据库丢了,除了重装系统没有其他后路。dpkg 数据库损坏虽然要小心,但几乎从不需要靠删库解决。用 apt 自带的修复链路,比暴力做法安全得多,只是需要多一点点耐心。
5.2 锁文件不能删:先学会看是谁锁的
报错里很常见的一条是E: Could not get lock /var/lib/dpkg/lock-frontend。它的意思是"有另一个包管理进程正在操作 dpkg 数据库,请你等它跑完"。最常见的场景就是后台无人值守更新进程还在跑,你却又手动启动了另一个安装命令。
我推荐先执行ps aux | grep -E 'apt|dpkg'找到占用进程,有时间就等它结束;等不了就sudo kill掉那个进程;进程结束后如果还发现锁文件残留,再考虑删 /var/lib/dpkg/lock*。删锁是最后手段,不是第一步。很多人一上手就删锁,结果两个包管理进程同时写数据库,把 dpkg 状态直接写坏,代价比等几分钟大得多。如果是虚拟机或云主机,还可以用lsof /var/lib/dpkg/lock-frontend看看是哪个 PID 在占用,信息更明确。
5.3 NO_PUBKEY 与密钥过期:别用 apt-key 一劳永逸
换源后最常遇到的报错是公钥缺失,提示一般是The following signatures couldn't be verified because the public key is not available: NO_PUBKEY 3DEF....。处理办法不是去搜"怎么关掉校验证签",而是把缺的公钥导入。老教程喜欢让你执行sudo apt-key adv --keyserver keyserver.ubuntu.com --recv-keys 那一串十六进制,但 apt-key 在新版 Debian/Ubuntu 上已经被标记为废弃,因为它把第三方密钥直接塞进系统全局信任区,风险大。
推荐做法是:从对应源官网下载专用的 .asc 或 .gpg 文件,放到 /usr/share/keyrings/ 目录,然后修改源配置里的signed-by=引用这个文件。这样每个源使用自己独立的密钥文件,删除源时密钥也跟着隔离。我在一台旧服务器上曾经图省事一直用 apt-key,后来某次升级之后系统 SSL 行为变得很奇怪,排查半天才发现是某个第三方仓库的 keyring 被全量信任后,和系统证书链搅到了一起。从那以后,我所有源配置都改成 signed-by 方案,配置时间多花两分钟,后续排障省两小时。
5.4 一次真实的依赖冲突排查:两个源抢同一个包
分享一段我实际排查到半夜的经历。某台 Debian 12 机器加了一个第三方软件源,那个源里打包了一个较新的 libx 相关库,和官方源同时提供同名文件。某次升级后,系统出现了一堆"无法满足依赖"的报错,主要表现是某个桌面组件要求旧版本库,而系统里的实际库已经被升到新版本。
我的排查步骤是这样的:先看报错涉及的包名,确认是桌面组件对旧版本有硬性依赖;然后执行apt-cache policy 包名看候选来源和版本优先级,一眼就发现第三方源里的版本领先官方源;接着把那行第三方源暂时注释掉,apt update刷新索引,再apt --fix-broken install让依赖回到可满足状态;最后重新安装目标应用,确认无误后再把第三方源恢复,但只允许它提供我需要的那几个包。
这场风波的教训是:第三方源和官方源同时启用时,要意识到这些源里同名包的版本会交错,必要时用版本锁定工具把关键包固定住。Debian 系用apt-mark hold 包名,Red Hat 系用dnf versionlock,Arch 系可以在 pacman.conf 里配置 IgnorePkg。一句口诀:改了什么,就记住什么;记不住,就少改。
6. Flatpak、Snap 和 AppImage:包管理器之外的另一条路
6.1 三种格式的共同逻辑:把依赖一起带走
桌面软件生态这些年有一个趋势:与其让发行版维护者去协调所有依赖版本,不如让应用打包者把运行环境一起带过来。Flatpak、Snap、AppImage 就是三条最有代表性的路径。它们的共同特点是:应用和它的运行时库被封装在一起,安装到用户空间,不干预系统目录,因此不同应用可以同时使用不同版本的同一个库,互不冲突。代价是体积变大、启动稍慢、磁盘占用更高。
对普通用户来说,最大的意义是"装 GUI 软件终于简单了"。比如在某个发行版上想装一个只有 Flathub 才有的软件,flatpak install一下就能跑,不用折腾系统依赖,也不会把系统搞坏。不过要清醒一点:它们不是包管理器的替代品,更像是一种"应用商店式"的补充分发渠道。想装上系统级的命令行工具时,官方仓库仍然是首选,因为命令行工具和系统库共享的边界很深,沙箱隔离反而会引入新的麻烦。
6.2 实际操作:Flathub 配置、Snap 生命周期与 AppImage 一键运行
Flatpak 第一步是添加 Flathub 源,命令是:
flatpak remote-add --if-not-exists flathub https://flathub.org/repo/flathub.flatpakrepo添加之后全局搜索flatpak search 关键词,找到应用的 ID,格式类似 org.xxx.App,而不是普通的名字。安装用flatpak install flathub org.xxx.App,运行用flatpak run org.xxx.App,卸载用flatpak uninstall org.xxx.App,清理不再使用的运行时用flatpak uninstall --unused。这个反向域名式的 ID 不太好记,我一般会用笔记记下来,或者直接查一次装一次。
Snap 在 Ubuntu 上开箱即用,sudo snap install 包名,更新是自动的,也可以用sudo snap refresh手动刷新。对带宽敏感或者想离线管控的场景,Snap 的自动更新机制会有点烦,但可以配置刷新时间窗来缓解。AppImage 更简单:下载一个 .AppImage 文件后,chmod +x直接运行,不需要任何安装步骤。如果要放进应用菜单,需要自己创建 .desktop 文件并指定图标路径。AppImage 的优点是完全不污染系统,缺点是每次下载都像一个独立小系统,升级也需要手动去拉新文件。
6.3 什么时候用哪种格式:一个老用户的取舍
我现在的取舍原则大致是:命令行工具、编程脚手架这类依赖系统库的工具,优先官方仓库或对应语言的版本管理工具;桌面 GUI,尤其官方只发布 Flatpak 版本的应用,优先 Flatpak;Snap 在 Ubuntu 生态里集成最顺,但在其他发行版上我会先装 Flatpak;AppImage 用来跑"不想安装进系统"的单文件工具,用完直接删。资源紧张的老机器上,我会尽量避开 Snap 和 Flatpak,因为它俩带的运行时常驻内存偏高,对老机器的负担比原生包明显大。
另外无论哪种格式,都要注意来源可信度。Flatpak 依赖 Flathub 的签名体系,Snap 有商店侧签名,AppImage 则没有内置签名验证机制,所以我只从项目官方发布页下载 AppImage,看到第三方聚合站的东西会先核对哈希值。这条建议和系统包管理器的 GPG 校验原则一脉相承:软件管理这件事,安全永远要让位于便利的对立面。
我在实际使用中发现,把 Linux 下的软件管理理顺,靠的不是记命令,而是记住一条原则:任何一次安装动作,都要清楚它写到了系统的哪个位置、由谁来记录、未来靠什么卸载。系统包管理器安装的,卸载交给包管理器;自己编译安装的,位置和依赖自己记账;Flatpak 这类容器化格式,交给运行时管理。三者混着来没问题,但千万要避免"包管理器装一个 A,源码又装一个 A,还都想抢同一个命令名",一旦路径优先级不对,你打开的根本不是你以为的那个版本。
最后再分享一个小技巧:所有系统配置层面的改动,换源、加源、锁定版本、临时禁用某个仓库,最好同步记在一个笔记里。系统崩了、依赖打起架来的时候翻一翻,十次有九次能秒破案。Linux 的软件管理并不可怕,可怕的是记不住自己改过什么。