搞 Linux 运维的人,迟早会碰上这样的场景:yum 源临时拉不下来,dnf 也在报错,手头却只有一个孤零零的 .rpm 文件;或者某个服务今天早上起来状态不对,你想知道它到底装了没有、文件被改过没有、卸载干净没有。这个时候,平时被各类包管理器“包办”的 rpm 命令,反而是最可靠的后手。这篇文章不绕弯子,把 rpm 常用的安装、升级、查询、验证、卸载五类操作全部串一遍,配合接近真实输出的示例和踩坑提醒,既能给新手当速查手册,也能给老手查漏补缺。
我会按使用频率来安排内容,先解决“这东西怎么装上去”,再讲“装完怎么确认”,最后聊“出了问题怎么验证和卸载”。每一条命令我都会说清楚参数为什么这么写,以及生产环境里哪些做法容易埋雷。
1. 先搞清楚:为什么各种包管理工具都出来了,还是要单独学 rpm
很多人一开始接触红帽系发行版时,装软件第一反应就是 yum install 或 dnf install,后来容器化普及,连装包都不太碰了。于是 rpm 这个底层的家伙被越埋越深。但实际上,yum 和 dnf 都只是“依赖解析器”,它们做的是把仓库里的 rpm 包拉下来,然后调用 rpm 程序完成真正的安装动作。换句话说,rpm 才是那个真正动手往系统里写文件、改数据库的底层工具。
1.1 只有 rpm 能回答的几类问题
当你有一天遇到这些疑问,就会发现包管理器帮不了你:某个二进制文件到底属于哪个包?某个包装完后往系统里放了哪些文件?一个 .rpm 文件在没安装前,包含哪些内容和依赖?本机已经装了哪些版本的包?这些问题的答案全部来自 rpm 的数据库,也只有 rpm 系列命令能查。
依赖解析器帮你解决问题时是“黑盒”的:它知道要装哪些依赖,但它不会告诉你为什么某个包升级会连带一堆别的包。而 rpm -q --requires 这种查询方式,能直接看到包的依赖列表,理解系统的“连接关系”。所以,即便日常安装都用 yum/dnf,我也建议你把 rpm 查询这套命令练熟,排查问题时非常快。
1.2 RPM 包命名规则里藏着的信息
看一个 rpm 包的名字,先看这串:demo-app-2.3-1.x86_64.rpm,它其实是四段,可以拆成:
demo-app:软件名称,也就是包名。2.3:软件本身的版本号,由上游定义。1:发布号,也叫 release,表示这个软件版本被这个发行版打包了几次。同一个 2.3 版本,release 不同,可能代表修复了打包层的问题或打了补丁,不能简单看成同一个包。x86_64:架构。常见还有i686、aarch64、noarch。如果架构不匹配,安装时会直接报错。noarch表示不依赖特定硬件架构,常见于脚本、纯配置文件、字体包。
理解命名规则能免掉很多无意义的报错。比如在一台 64 位机器上装 i686 的包,大概率能装上,但可能因为依赖和系统里已装的 x86_64 版本冲突,运行出各种怪问题。还有 release 的区别:如果你用-Uvh升级一个 release 号更低的包,rpm 会认为你试图“降级”,不加参数通常会拒绝操作,这也是一种安全保护。
2. 安装和升级:-i 和 -U 的区别,用错会吃亏
安装和升级是 rpm 用得最频繁的两类操作,但很多人并不清楚-i和-U到底差在哪。简单说:-i是纯粹安装,适合第一次装;-U是升级或安装,如果系统里已经有旧版本,它会把旧的替换掉,如果没有旧版本,它也会直接装新包。所以日常操作我建议你优先用-Uvh,它覆盖的场景更多,也更符合直觉。
2.1 安装命令 -ivh 的完整参数含义
rpm -ivh demo-app-2.3-1.x86_64.rpm是最标准的写法。其中:
-i:install,执行安装。-v:verbose,输出详细过程。-h:hash,用#号显示进度条,尤其是安装大包时,能直观看到进度。
一个正常安装的输出大概是这样的:
Preparing... ################################# [100%] Updating / installing... 1:demo-app-2.3-1 ################################# [100%]这里有个容易被忽略的点:安装完成后,rpm 不会自动启动服务,也不会帮你处理配置文件冲突。如果这个包里带了 systemd unit 文件,你装完通常还要手动systemctl enable --now xxx。这一点和 Debian 系的部分包安装后自动启动服务的习惯不一样,刚切换过来的人总以为包坏了。
2.2 升级用 -Uvh 还是 -ivh?关键差异要分清
假设系统里已经装了 demo-app-2.2,现在手里拿的是 demo-app-2.3,你会用哪个命令?
- 用
-ivh:因为版本号比旧的更大,rpm 会报“already installed”,并且不给装,因为它默认不接受“降级”或覆盖已存在的新版本。实际上有旧版本存在时,-i会拒绝安装整个包。 - 用
-Uvh:会自动把系统里已有的 demo-app 旧版替换成新版,不依赖手动卸载旧包。
还有另一个参数-Fvh(freshen),它表示“仅当系统已经有旧版本时才升级,如果没有这个包就什么都不做”。这个适合批量场景:你想在一批机器上统一把某个软件升级到最新,但其中有几台机器根本没装这个软件,-F会安静跳过,而-U则会顺手装上,造成“没装的机器反而多了个软件”的局面。
所以你问我日常用哪个,我一般默认-Uvh。除非我明确知道这台机器绝对没有这个包,并且不希望自动升级,才会用-ivh。另外,rpm -Uvh --oldpackage可以在某些场景下强制降级,但生产环境要非常克制,只有在紧急回滚时才用。
2.3 依赖不足时的三个选择,以及每个选择的代价
rpm 安装最常遇到的报错是这样:
error: Failed dependencies: libmybase.so.1()(64bit) is needed by demo-app-2.3-1.x86_64简单说,这个包需要某个共享库或能力,而系统当前没有。这时你有三条路:
- 先装上依赖包,然后再装主包。这是最稳的,离线环境也是 Backlog 顺序。
- 用
--nodeps强行跳过依赖检查。能装上,但大概率运行时提示error while loading shared libraries: libmybase.so.1: cannot open shared object file,等于装了个半成品。 - 用
--force强行安装。这个更危险,它会忽略版本冲突、依赖缺失等问题直接覆盖,可能把系统里已有包的文件弄乱。
很多教程喜欢写“缺依赖就加 --nodeps,装不上就加 --force”,我强烈不建议无脑抄。依赖缺失通常才是真相,盲目跳过会让系统进入很脏的状态。遇到依赖问题,正确路径是先用rpm -qpR demo-app-2.3-1.x86_64.rpm看这个包究竟需要什么,然后找对应的 rpm 包把依赖补齐。离线介质里一般都能在安装包的 mounts 目录下找到依赖,先装依赖再装主包,顺序别颠倒。
3. 查询:最常用、最救命的一类命令
如果说安装和升级是“动手”,那查询就是“动脑”。排查系统问题时,90% 的时间都在查 rpm 数据库。这类命令不会对系统造成任何改动,你可以放心大胆敲,也是我建议新手最先练熟的部分。
3.1 查已装包:-qa、-qi、-ql 各解决什么问题
最基础的是rpm -q 包名,比如:
rpm -q demo-app如果装了会返回类似demo-app-2.3-1.x86_64,如果没装会返回package demo-app is not installed。
但实际场景里,你往往记不全包名,这时候rpm -qa就派上用场了。它列出系统所有已安装的 rpm 包,数量可能上千。通常会配合 grep 过滤:
rpm -qa | grep demo这里有一个细节值得反复强调:rpm -q后面的参数是“包名”,不是文件名,也不带版本号。很多人拿到一个包名字demo-app-2.3-1.x86_64.rpm,直接rpm -q demo-app-2.3,这样是查不到的。rpm 数据库里记录的名字是demo-app,版本号是单独的字段。你只需要写包名,它自己会匹配最新版本。
其他几个高频查询参数:
rpm -qi demo-app:查看包的详细信息,包括安装日期、License、构建主机、安装大小、摘要描述。适合确认这台机器上到底装的是哪个版本,谁打的包,什么时候装的。rpm -ql demo-app:列出这个包安装后往系统写入的所有文件绝对路径。查路径、找配置文件,都靠这个。rpm -qc demo-app:只列出配置文件,通常位于/etc。rpm -qd demo-app:只列出文档文件,通常在/usr/share/doc下。rpm -q --requires demo-app:列出这个包运行依赖的库和命令。rpm -q --provides demo-app:列出这个包对外提供的能力,其他包依赖的就是这些名字。
我自己的习惯是,只要有人跟我说“某个服务起不来”,第一板斧永远是rpm -q 服务名加rpm -ql 服务名 | head,先确认软件确实装了,再看二进制和配置文件在位不在位,比去看日志都快。
3.2 查文件归属与未安装包信息
当你看到一个陌生文件,比如/opt/weird/bin/tool,想知道它是谁装的,用:
rpm -qf /opt/weird/bin/tool这个命令通过文件路径反查 rpm 数据库。前提是文件确实由某个 rpm 包安装,且路径精确。如果文件被移动过、或者不是 rpm 包安装的(比如编译安装),会提示file is not owned by any package。
另一个高频场景是:拿到一个 .rpm 文件但还没安装,想知道里面装了些什么,是否可以安全安装。这时不能在包名位置用文件名,而是加一个-p参数,p 代表 package file:
rpm -qip demo-app-2.3-1.x86_64.rpm rpm -qlp demo-app-2.3-1.x86_64.rpm rpm -qp --requires demo-app-2.3-1.x86_64.rpm注意,这个-p必须是配合-q来用。它会读取 rpm 文件头,而不是查系统数据库。用它在安装前检查依赖、确认文件路径,能避免很多后面要回滚的麻烦。
3.3 查询命令的典型场景串联
下面这个场景我很常见:一台机器上有个服务叫datasvc,今天异常退出。我的排查顺序大致是:
rpm -q datasvc rpm -ql datasvc | grep -E 'bin|etc' rpm -qc datasvc rpm -V datasvc先用-q确认版本;再用-ql找到二进制和配置文件位置;-qc直接列出配置文件;最后-V验证文件有没有被动过。整个流程下来不需要看一行日志,就能判断“是不是软件被改坏了”还是“配置被覆盖了”。在团队协作时,你把这三条命令的输出贴给同事,对方基本能快速定位问题,这比讲半天“我怀疑”有用得多。
4. 验证:确认系统里的文件还是“原装货”
验证是我个人觉得最被低估的一个功能。系统被入侵、升级异常、误改二进制、运行库被替换,这类问题平时风平浪静时根本不会暴露,等出事时想反查证据,rpm -V就是那个最直接的“体检报告”。
4.1 rpm -V 的输出每个字符意味着什么
先看命令:
rpm -V demo-app如果所有文件都没有改动,输出为空,没有消息就是好消息。一旦有改动,每个被改过的文件前会有一串形如S.5....T.的标记,再加文件路径。这串标记一共有 9 个字符,每个字符表示一种属性对比结果,没变化的用.表示。常见的几个含义:
| 标记 | 位置 | 含义 |
|---|---|---|
| S | 第1位 | 文件大小被改变 |
| M | 第2位 | 权限或文件类型被改变 |
| 5 | 第3位 | MD5 校验和不一致,内容被改动 |
| D | 第4位 | 设备主次号变化 |
| L | 第5位 | 符号链接指向变化 |
| U | 第6位 | 属主变化 |
| G | 第7位 | 组属主变化 |
| T | 第8位 | 修改时间变化 |
| P | 第9位 | 能力(capabilities)变化 |
举个例子,你看到输出:
S.5....T. /usr/bin/demo-app说明这个文件的体积变了,内容校验不通过,时间戳也变了。这种情况基本可以断定二进制被替换过。注意它不告诉你“改了什么内容”,只告诉你“和安装时相比变了”,但这已经足够启动排查了。
如果想验证某个具体文件,而不是整个包:
rpm -Vf /usr/bin/demo-app-Vf里的f表示 file,反查这个文件属于哪个包,然后只验证这一个文件。这个用法在跑批量巡检的时候非常实用,可以写个脚本循环验证/usr/bin或/usr/sbin下的关键二进制。
4.2 签名验证与 GPG Key 的实操细节
验证除了检查文件属性和内容,还有一个层面是检查包本身有没有被篡改,尤其是从网上下载的 .rpm 文件。rpm 包可以携带基于 GPG 的数字签名,安装时如果本地导入了对应公钥,会自动做签名校验。手动检查用:
rpm -K demo-app-2.3-1.x86_64.rpm输出如果有digests signatures OK,表示摘要和签名都正常。如果提示MISSING KEYS,则说明包里带有签名,但系统没导入对应的公钥,这时候导出包的一方会提供公钥文件,通常叫RPM-GPG-KEY之类,导入用:
rpm --import RPM-GPG-KEY网上下的包,如果来源比较杂,我建议至少先用sha256sum对比官方给的值。签名验证这一步在多数企业里其实没那么严格,真到出了安全事件,这个坑能不能填上就看平时有没有做。我的建议是:对下载来的包,养成先rpm -K再安装的习惯,成本极低,换来的是安心。
4.3 验证失败后的排查流程
rpm -V报错不等于系统已经完蛋,要先分级看待。配置文件被人按需修改过,比如/etc/xxx.conf内容微调,这在生产中非常正常,-V一定会报S.5....T.之类的输出。对配置文件的“改动”不必恐慌,那是运维日常。
真正要警惕的是三类:二进制文件报 MD5 变化、系统关键库文件报内容变化、本该在/etc的文件出现属主或权限异常。遇到这种,我建议按下面顺序处理:
- 用
rpm -qf找出这个文件属于哪个包。 - 用
rpm -ql确认这个包同时管理了哪些其他文件,检查是否还有其他异常。 - 对照同版本系统的正常文件,确认具体是什么改动。
- 如果是明显被入侵或误改,用
rpm -Uvh --force重新覆盖安装这个包来恢复文件。
这里有个提醒:--force重装会把包里的文件按原样覆盖回来,但如果你对配置文件做过重要修改,重装前一定先备份。很多人在应急恢复时顺手rpm -Uvh --force,装完才发现自己写的配置全被覆盖了,那是另一场灾难。
5. 卸载:简单背后也有顺序问题
卸载是 rpm 操作里看起来最简单但实际最容易出事的一类。原因在于,一条rpm -e可能因为依赖关系牵连到其他包。
5.1 -e 卸载的常规操作与依赖关系
rpm -e demo-app注意,卸载用的也是“包名”,不是 .rpm 文件名。执行后 rpm 会依据数据库记录删除文件。如果这个包里有配置文件,而且你改过它,rpm 通常会保留一份,改名为demo-app.conf.rpmsave或类似的后缀。反过来,如果从没改过,它会直接把文件删掉。这个机制很多人不清楚,卸载后在/etc底下看到残留文件以为没卸干净,其实那是 rpm 的善意保留。
卸载时最常见的报错是:
error: Failed dependencies: demo-app is needed by (installed) proj-ext-1.0-1.x86_64意思是当前系统里有一个叫proj-ext的包依赖 demo-app,如果直接卸载,proj-ext 就会变成坏包。这时候有两个选择:先卸载依赖它的包,再卸载 demo-app;或者用rpm -e --nodeps demo-app强卸,但要承担 proj-ext 运行不了的风险。
5.2 卸载失败、卸载后再安装的常见坑
卸载失败还有一个容易被忽略的原因:你记错了包名。比如实际包名是demo-app,你执行rpm -e demo-app-2.3,也有可能成功,因为 rpm 允许用 name-version 形式。但如果连 release 也写上,有时会由于版本写得过死导致匹配不到。最稳妥的办法就是只写包名,不要带版本号。
卸载后再重装,也会遇到一些怪现象。一种情况是rpm -e已经把文件删了,但配置文件的.rpmsave残留在原路径,重装后被当成普通配置文件保留下来。另一种情况是卸载时报错说包不存在,但rpm -qa | grep xxx又能查到,这往往是你 grep 的关键词太宽,把别的包带出来了,比如demo-app-libs和demo-app是两个不同包,你以为没卸干净,其实卸的只是其中之一。
生产环境里,我建议卸载前先执行rpm -q --requires的反向查询:
rpm -e --test demo-app--test是试运行,它不会真的删除,但会告诉你“如果我卸载会碰到哪些依赖问题”。这样你可以提前决定处理顺序,而不是等报错出来再手忙脚乱。
6. 日常运维常用的 rpm 组合套路
最后整理一份我平时真正高频使用的命令表。它不是一个完整的 rpm 手册,只能说覆盖了九成运维场景。每次排查问题我都会从这里挑几条先跑。
6.1 几条高频组合命令
| 场景 | 命令 |
|---|---|
| 安装本地包 | rpm -ivh demo-app-2.3-1.x86_64.rpm |
| 升级或安装包 | rpm -Uvh demo-app-2.4-1.x86_64.rpm |
| 批量升级指定条件包 | rpm -Fvh *.rpm |
| 列出已安装包 | rpm -qa |
| 带关键词查找已装包 | rpm -qa | grep demo |
| 查看单个包信息 | rpm -qi demo-app |
| 查看包文件清单 | rpm -ql demo-app |
| 反查文件归属 | rpm -qf /usr/bin/demo-app |
| 验证整个包 | rpm -V demo-app |
| 验证单个文件 | rpm -Vf /usr/bin/demo-app |
| 检查未安装的包文件 | rpm -qlp demo-app-2.4-1.x86_64.rpm |
| 查看包依赖 | rpm -qp --requires demo-app-2.4-1.x86_64.rpm |
| 卸载包 | rpm -e demo-app |
| 试卸载(模拟) | rpm -e --test demo-app |
| 校验包数字签名 | rpm -K demo-app-2.4-1.x86_64.rpm |
这张表我建议你存在本地便签里。日常大部分查询,rpm -qa | grep和rpm -ql就能解决;真涉及变更,-Uvh和-e记得加--test先模拟一下。
6.2 我踩过的几个坑和现在的习惯
说几个真实让我吃过亏的点,希望你别再走一遍。
第一,永远不要在一个关键的库包上乱用--nodeps。有次我给一台机器装新版本的基础库,离线环境少了一个依赖,图省事加了--nodeps,结果系统里一堆命令行工具立刻开始报动态库错误,那台机器基本处于半瘫痪状态。后来花了很长时间从另一台同版本机器上拷贝原版库文件才救回来。从那以后,我发现任何报缺依赖的情况,第一反应都改成“去找依赖”,而不是“跳过依赖”。
第二,升级前养成备份数据库的习惯。rpm 操作记录在/var/lib/rpm,虽然现代版本已经是 sqlite 结构,但依然可能因为操作异常被锁住或损坏。我现在的习惯是,在生产机做大规模 rpm 操作前,先cp -a /var/lib/rpm /var/lib/rpm.bak.$(date +%F),或者更省事的,在确认包清单后先跑一次rpm -qa > /tmp/rpm-qa-before.txt。后者至少能让你知道操作前系统装了什么,排查问题时能从“状态变化”入手。
第三,处理大量 rpm 包时别裸跑rpm -ivh *.rpm。因为 rpm 按文件名顺序处理,不会自动依赖排序,很可能因为你把依赖包放在后面而先报错。你可以用rpm -Uvh *.rpm,它会先做依赖分析,自动调整安装顺序,比-ivh更适合同目录多包安装。
最后再分享一个判断技巧:当你怀疑某个 rpm 包的文件被改过,但又不确定是不是自己改的,可以先用rpm -V找出变化的文件,再用rpm -ql看它原本应该是什么。如果变化的是配置文件,那大概率是正常维护留下的痕迹;如果变化的是二进制和系统库,那就要立刻重视起来。rpm 本身不会告诉你这个文件“该不该变”,它只负责忠实地告诉你“和装的时候不一样了”。真正的判断,还是要靠你对系统状态的熟悉程度。所以平时多看、多查、多留底,等真出事的时候,这份积累比任何工具都值钱。