RPM重打包实战:从拆包到构建的完整技术指南

发布时间:2026/10/9 4:57:28
RPM重打包实战:从拆包到构建的完整技术指南
1. 项目概述为什么“repack RPM包”是运维和打包工程师绕不开的基本功你有没有遇到过这样的场景线上某台服务器上跑着一个老版本的Nginx它被某个定制化补丁打了多年但原始RPM包早已下线官方仓库里只有新版——而新版又因为ABI不兼容导致下游服务启动失败或者你接手了一个遗留系统它的安装包依赖一个内部编译的OpenSSL 1.1.1w静态链接库但所有公开渠道的RPM都只带1.1.1t连dnf download --source都拉不到对应spec再比如安全团队突然下发紧急通告要求所有Java应用必须使用JDK 17.0.910-LTS含特定CVE修复可你手头的java-17-openjdkRPM包版本号是17.0.8.0.7-2.el9_2差了整整一个微版本dnf update根本推不上去。这时候“重打包repack一个RPM包”就不是可选项而是保命操作。所谓repack不是简单地解压再压缩而是在保持原有二进制内容、文件路径、权限、依赖声明完全不变的前提下仅修改其元数据version/release/epoch、签名信息、构建时间戳、甚至嵌入新补丁或替换个别配置文件并重新生成符合RPM数据库校验逻辑的合法二进制包。它比rpm -Uvh安装更底层比dnf builddep更可控也比直接用cpio硬改包体更安全可靠。核心关键词repack、RPM、rpmbuild、rpm2cpio、spec每一个都不是孤立存在rpm2cpio是拆包的起点spec是重打包的灵魂蓝图rpmbuild是最终落锤的铸模机而repack本身则是一套完整的手工精密装配流程。这个操作天然面向三类人一是企业内负责中间件统一交付的平台工程师他们需要把上游社区包“贴牌”成内部标准命名规范二是安全合规岗人员他们要对已知漏洞版本打热补丁并快速生成可审计的RPM三是嵌入式或信创环境下的适配工程师他们得把x86_64编译好的包无损迁移到aarch64或loongarch64平台。它不追求炫技但极度讲究细节——一个空格写错在spec的Version:字段后就会触发invalid versionspec error: 2.7这种看似诡异实则精准的报错少写一行%files声明安装时就会提示file /usr/bin/mytool from install of mypkg-1.0-1.el9.x86_64 conflicts with file from package xxx而如果你在CentOS Stream 9上执行rpmbuild却没装rpm-build和rpmdevtools那连rpmbuild命令都找不到更别说rpm命令本身——这正是“没找到rpm命令”背后最常被忽略的基础环境缺失。我做过不下二十次RPM重打包从给MySQL 8.0.33打国密SM4加密插件补丁到为某国产数据库适配麒麟V10的glibc 2.34 ABI再到把Red Hat提供的kernel-rt源码包降级回退到5.10.162-rt77版本以满足实时性SLA。每一次我都坚持一个铁律所有改动必须可追溯、可复现、可验证。这意味着不能靠rpm -ivh --force硬顶也不能用alien转deb再转回rpm——那些都是临时止痛药。真正的repack是从rpm2cpio开始到rpmbuild -bb结束中间每一步都有明确目的、可检查输出、有备份机制。接下来我会带你走完这条完整的、没有捷径的路。2. repack全流程设计与关键决策点解析2.1 为什么不用“解压修改重打包”这种粗暴方式很多刚接触RPM的人第一反应是既然RPM本质是个cpio归档那我用rpm2cpio xxx.rpm | cpio -idmv解出来改完文件再用find . | cpio -o -H newc | gzip new.rpm不就完了答案是否定的。原因有三第一RPM不是普通归档它包含四层结构头部header存储元数据name/version/release/arch/dependencies等、签名区signature用于GPG校验、文件头区lead signature header记录每个文件的校验和、权限、属主最后才是实际文件数据区payload。rpm2cpio只提取payload丢失了全部元数据和签名。你手动打包出来的.rpm文件rpm -qpi看不出来任何信息rpm -K校验直接失败dnf install会拒绝加载因为它根本不认识这个“假包”。第二RPM数据库/var/lib/rpm/在安装时会将包头信息写入BDB或SQLite数据库。如果包头缺失或格式错误安装过程会在transaction set阶段崩溃报错类似error: rpmdb: BDB2053 Could not allocate memory这不是内存问题而是header解析失败的伪装。第三现代RPM尤其是RHEL 8、Fedora 35默认启用%_pkgverify_level all强制校验所有文件的SHA256、大小、mtime、mode、owner、group六项属性。你手工改完一个配置文件却不更新对应的header中该文件的校验和记录rpm -V验证时立刻暴露“S.5....T. c /etc/myapp.conf”其中S代表文件大小变了5代表MD5或SHA256变了T代表mtime变了——全军覆没。所以正确的repack路径只有一条通过spec文件驱动rpmbuild让工具链自动完成header生成、签名嵌入、payload压缩、数据库兼容性校验全过程。rpm2cpio只是第一步的“探针”用来确认原始包里到底有什么rpmbuild才是真正的“手术刀”它依据spec定义的规则一比一复刻原始结构只允许你动指定的几处。2.2 两种主流repack策略对比Source-based vs Binary-based根据原始RPM是否提供源码包src.rpmrepack分为两条技术路线维度Source-based Repack推荐Binary-based Repack应急前提条件必须拿到原始src.rpm如nginx-1.20.1-10.el9.src.rpm只需原始二进制rpm如nginx-1.20.1-10.el9.x86_64.rpm核心工具rpm -i,rpmbuild -bp,rpmbuild -bbrpm2cpio,cpio,rpmdev-setuptree,rpmbuild -bb改动自由度高可修改spec、打补丁、替换tarball、调整buildroot路径低只能改spec中version/release/patches无法替换二进制主体可审计性极高所有变更都在spec和patch文件中git可追踪中部分改动如直接改解压出的bin文件无法体现于spec适用场景长期维护、合规审计、CI/CD集成、多平台交叉编译紧急热修复、单点故障恢复、无源码环境如闭源商业软件绝大多数情况下我首选Source-based。因为src.rpm里已经包含了完整的spec文件、所有补丁.patch、源码压缩包.tar.gz/.tar.xz你只需要rpm -i安装到~/rpmbuild/目录下整个构建树就自动搭好了。而Binary-based虽然快但风险极高你解包后看到的/usr/bin/nginx是编译好的二进制如果想给它打一个动态链接库劫持补丁就必须用patchelf改DT_RPATH这属于二进制层面hack一旦glibc升级或内核参数变化随时可能崩溃。我曾经在一个金融客户现场用Binary-based给某监控agent打日志路径补丁结果上线三天后因SELinux策略收紧patchelf修改的rpath被avc denail拦截整个agent静默退出——这种问题Source-based从源头就能规避你在spec里加一行%global _hardened_build 0关掉PIE再用%patch1001 -p1打标准补丁编译时就天然适配。2.3 spec文件repack的唯一真相之源很多人以为spec文件就是个“说明书”改几个变量就行。错。spec是RPM构建的状态机定义语言它控制着整个生命周期从准备源码%prep、编译%build、安装到buildroot%install到最终打包%files、清理%clean。任何一个section的执行顺序、环境变量、shell上下文都严格受rpmbuild引擎约束。举个真实例子某次我需要把mysql-community-server-8.0.33-1.el9.x86_64.rpm降级到8.0.32同时加入一个自研的审计插件。原始spec里有这样一段%build %cmake \ -DCMAKE_INSTALL_PREFIX%{_prefix} \ -DWITH_SSLsystem \ -DDEFAULT_CHARSETutf8mb4 \ -DDEFAULT_COLLATIONutf8mb4_0900_ai_ci \ %{nil} make %{?_smp_mflags}如果我只改Version: 8.0.32不碰%buildrpmbuild会去下载8.0.32的源码tarball但CMake参数里-DDEFAULT_COLLATIONutf8mb4_0900_ai_ci在8.0.32中根本不存在它是8.0.33新增的编译直接报错CMake Error at cmake/mysql_version.cmake:180 (message): Unknown collation。这时候我就必须同步修改%build段删掉这行或者加个条件判断%if 0%{?rhel} 9 %cmake -DDEFAULT_COLLATIONutf8mb4_0900_ai_ci %{nil} %else %cmake -DDEFAULT_COLLATIONutf8mb4_general_ci %{nil} %endif这就是spec的威力它不是静态文本而是带逻辑的构建脚本。%if、%define、%global、%{?dist}这些宏让同一个spec能适配RHEL、CentOS、AlmaLinux、Rocky Linux多个发行版。而invalid versionspec error: 2.7这类报错往往就源于宏展开后Version:字段变成了Version: 2.7——注意那个等号它是RPM spec语法里的非法字符正确写法是Version: 2.7前面不能有任何符号。rpmbuild在解析时会严格校验发现开头就直接抛异常不会给你任何机会。2.4 工具链选型为什么坚持用原生rpmbuild而非第三方包装器网上有很多“一键repack”脚本比如用Python调用subprocess执行rpm2cpiocpiorpmbuild或者用Ansible playbook封装整个流程。我试过三个主流方案最终全部弃用方案Arpmrebuild它确实能rpmrebuild -e -p newpkg.rpm直接打开spec编辑。但问题在于它生成的spec是“反向工程”出来的很多宏如%{?_with_systemd}会被展开成硬编码值%configure宏变成一长串./configure参数可读性极差。更致命的是它不支持%autosetup无法处理多补丁叠加场景。方案Bmock chrootmock能提供干净的构建环境避免host系统污染。但它太重每次都要下载完整rootfs镜像500MB启动chroot耗时30秒以上。对于需要高频迭代的repack比如一天改十次spec测兼容性效率低下。方案Cpodman run -v $PWD:/workspace quay.io/centos/centos:stream9 rpmbuild...容器化思路没错但实际运行时发现podman默认不挂载/proc和/sys导致rpmbuild在%check阶段调用systemctl失败且容器内~/.rpmmacros路径和host不一致宏定义丢失。所以我回归最原始的方式在目标发行版的干净虚拟机里手动安装development-tools组再装rpm-build rpmdevtools yum-utils然后rpmdev-setuptree初始化~/rpmbuild。所有操作都在本地完成rpmbuild -ba --define _topdir %(pwd)/rpmbuild mypkg.spec一条命令搞定。虽然初始setup多花5分钟但后续每次rpmbuild -bb只要3秒且100%可复现。工具越简单出问题时越容易定位——这是十年运维给我最深的教训。3. 核心细节解析与实操要点3.1 拆包溯源rpm2cpio不是万能钥匙而是第一道安检门rpm2cpio命令看起来简单但用错地方会埋下大坑。它的本质是把RPM包的payload部分即cpio archive输出到stdout供管道后续处理。但请注意它不校验包的完整性也不解析header。一个被恶意篡改过的RPMrpm2cpio照样能解出文件但你用它生成的spec去rpmbuild最后得到的包在rpm -K校验时必然失败。所以拆包前必做三件事校验原始包签名rpm -Kv original.rpm # 输出应包含 digests signatures OK若出现 NOKEY 或 NOTFOUND说明缺少公钥 # 此时需先导入上游GPG keyrpm --import /etc/pki/rpm-gpg/RPM-GPG-KEY-centosofficial确认包架构与发行版匹配rpm -qip original.rpm | grep -E (Name|Version|Release|Architecture|Vendor|Build Date) # 关键看Architecture是否为x86_64/aarch64Build Date是否早于当前系统内核 # 若Build Date是2020年而你的系统是RHEL 9.32023年发布glibc ABI可能不兼容提取payload并快速扫描敏感文件rpm2cpio original.rpm | cpio -idmv 2/dev/null # 解压后立即执行 find . -name *.so* -o -name *.a -o -name ld-linux* | xargs file 2/dev/null | grep ELF.*GNU/Linux # 确认所有动态库都是GNU/Linux ABI而非musl或FreeBSD # 同时检查是否有硬编码IP/域名grep -r 192\.168\|example\.com . 2/dev/null我曾在一个政府项目中用rpm2cpio解包一个声称是“国产化适配版”的Redis RPM结果在./usr/lib64/redis/modules/下发现一个libmemcached.so.11file显示它是ELF 64-bit LSB pie executable, x86-64, version 1 (SYSV), dynamically linked, BuildID[sha1]..., for GNU/Linux 3.2.0——这明显是Ubuntu 20.04编译的和麒麟V10的glibc 2.28不兼容。当场叫停repack流程要求供应商提供真实构建日志。rpm2cpio在这里不是工具而是取证工具。3.2 spec文件逆向工程如何从二进制包里“抠”出可用spec当只有二进制RPM没有src.rpm时spec必须自己写。这不是凭空造轮子而是“考古式还原”。步骤如下第一步用rpm -qpis 获取基础元数据rpm -qpis original.rpm metadata.txt # 输出包含Name, Version, Release, Architecture, Install Date, Group, Size, License, Signature, Source RPM, Build Host, Relocations, URL, Summary, Description # 其中Source RPM字段最关键它告诉你原始src.rpm名字比如nginx-1.20.1-10.el9.src.rpm第二步用rpm -qpl 获取文件列表反推%files段rpm -qpl original.rpm | sort files.list # 观察路径规律/usr/bin/ 开头的是可执行文件/etc/ 是配置/usr/lib64/ 是库/usr/share/doc/ 是文档 # 用awk生成初步%files awk /^\/usr\/bin\// {print %attr(0755,root,root) $1; next} /^\/etc\// {print %config(noreplace) $1; next} /^\/usr\/lib64\// {print %attr(0755,root,root) $1; next} /^\/usr\/share\/doc\// {print %doc $1; next} {print $1} files.list files.spec第三步用rpm -qpi 提取依赖填充Requires/BuildRequiresrpm -qpi original.rpm | grep -E (Requires|BuildRequires) | sed s/://g | awk {for(i2;iNF;i) print $i} | sort -u deps.list # 注意Requires里可能有python3 3.9要写成Requires: python3 3.9不能漏冒号 # BuildRequires通常不体现在二进制包里需根据%build段猜若有gcc调用就加BuildRequires: gcc第四步最关键的%prep段还原这是最难的部分。你需要从解压出的文件里找线索查看/usr/src/debug/下是否有debuginfo包残留如果有cat /usr/src/debug/*/Makefile能看到编译参数在/usr/share/doc/*/下找README、INSTALL文件里面常有./configure --prefix/usr ...字样用strings扫描二进制文件strings ./usr/bin/nginx | grep -i configure可能爆出完整configure命令我曾为一个闭源网关设备的RPM还原specstrings在/usr/bin/gatewayd里找到一行--with-http_ssl_module --with-http_v2_module --with-cc-opt-O2 -g -pipe -Wall -Wp,-D_FORTIFY_SOURCE2 -fexceptions这直接告诉我它用了OpenSSL且开启了fortify source保护。把这些碎片拼起来spec的骨架就完整了。3.3 版本号与Release字段那些让你栽跟头的隐藏规则RPM版本号Version和发布号Release不是随便写的字符串它们有严格的排序逻辑和语义约定。invalid versionspec error: 2.7就是典型违反规则的产物。Version字段规则只能包含ASCII字母、数字、点.、下划线_、加号、波浪号~绝对不能以等号、减号-、冒号:开头或结尾推荐格式1.2.3、2.7.10、3.14.159避免2.7-rc1减号非法应写成2.7~rc1波浪号表示预发布Release字段规则格式为release_number.dist_tag如1.el9、2.fc38、3.rocky9release_number是纯数字递增表示新版本1→2→3dist_tag标识发行版el9代表RHEL/CentOS Stream 9fc38代表Fedora 38如果你要打内部补丁Release应为1.1.internal而不是1.internal缺少数字前缀排序逻辑决定yum update谁优先RPM用rpmdev-vercmp比较版本规则是先按Version分段比较1.2.3→[1,2,3]1.10.0→[1,10,0]所以1.10.0 1.2.3Version相同时按Release分段比较1.el9→[1,el9]1.1.el9→[1,1,el9]所以1.1.el9 1.el9波浪号~最低优先级1.0~rc1 1.0因此如果你把Version写成2.7rpmbuild解析时会把当作分隔符试图把2.7当做一个独立段但不是合法字符直接报错。正确做法是Version: 2.7Release: 1.1.myorg。我在某次给MySQL打补丁时Release写成1.myorg结果dnf update永远不认新包因为1.myorg被解析为[1,myorg]而官方包是[1,el9]字符串比较myorg el9为真所以旧包反而更高——改成1.1.myorg立刻解决。3.4 补丁管理为什么不用git am而坚持用%patch宏给RPM打补丁新手常犯的错误是git clone源码git am 0001-fix-bug.patch然后git archive生成新tarball最后扔进SOURCES/。这看似合理但破坏了RPM的可重现性原则。RPM官方推荐方式是把补丁文件放在SOURCES/目录然后在spec的%prep段用%patch宏应用。例如Source1: fix-null-deref.patch ... %prep %autosetup -n %{name}-%{version} %patch1 -p1%autosetup会自动解压Source0主tarball%patch1则从SOURCES/fix-null-deref.patch读取用patch -p1应用。好处有三补丁可审计所有补丁文件都明文存放在SOURCES/git log能查到谁在什么时候加了什么补丁冲突可感知如果补丁应用失败rpmbuild会中断并报错patch failed: src/main.c:123而不是默默跳过版本可追溯rpm -q --changelog mypkg能显示* Mon Jan 01 2024 My Name memyorg.com - 1.0-1.1.myorg下面跟着- Apply fix-null-deref.patch to prevent crash on empty input我曾管理一个包含47个补丁的OpenSSL RPM如果每个都git am光是维护patch顺序就要花半天而用%patch只需在spec里按顺序写%patch1到%patch47rpmbuild自动按序应用%patch -P 47还能指定应用到第47个为止。这才是企业级打包该有的样子。4. 实操过程与核心环节实现4.1 完整repack流程从拿到原始RPM到生成新包假设你手上有一个mysql-community-server-8.0.33-1.el9.x86_64.rpm需求是降级到8.0.32加入审计插件audit_plugin.so并打上内部版本号1.1.myorg。以下是我在生产环境实测的完整步骤Step 1环境初始化一次性# 在RHEL 9.2虚拟机中执行 dnf groupinstall Development Tools dnf install rpm-build rpmdevtools yum-utils rpmdev-setuptree # 创建 ~/rpmbuild 目录结构 # 检查 ~/.rpmmacros 是否存在若无则创建内容 # %_topdir %(echo $HOME)/rpmbuild # %_smp_mflags -j$(/usr/bin/nproc)Step 2获取原始src.rpm关键# 方法1从官方repo下载推荐 dnf download --source mysql-community-server # 方法2若dnf找不到用yum-utils的yumdownloader yumdownloader --source mysql-community-server # 得到 mysql-community-server-8.0.33-1.el9.src.rpm rpm -i mysql-community-server-8.0.33-1.el9.src.rpm # 此时 ~/rpmbuild/SPECS/ 下有 mysql-community-server.spec # ~/rpmbuild/SOURCES/ 下有 mysql-8.0.33.tar.gz 和所有.patchStep 3准备新源码与补丁# 下载8.0.32源码 wget https://dev.mysql.com/get/Downloads/MySQL-8.0/mysql-8.0.32.tar.gz mv mysql-8.0.32.tar.gz ~/rpmbuild/SOURCES/ # 准备审计插件 cp /path/to/audit_plugin.so ~/rpmbuild/SOURCES/ # 编写补丁文件示例修改CMakeLists.txt加入插件 cat ~/rpmbuild/SOURCES/add-audit-plugin.patch EOF diff -Naur mysql-8.0.33/CMakeLists.txt mysql-8.0.32/CMakeLists.txt --- mysql-8.0.33/CMakeLists.txt 2023-05-15 10:00:00.000000000 0000 mysql-8.0.32/CMakeLists.txt 2023-05-15 10:01:00.000000000 0000 -123,6 123,7 INSTALL(FILES ${CMAKE_CURRENT_BINARY_DIR}/libmysqld.a DESTINATION lib) INSTALL(FILES ${CMAKE_CURRENT_BINARY_DIR}/libmysqld.so DESTINATION lib) INSTALL(FILES ${CMAKE_CURRENT_BINARY_DIR}/libmysqld.so.${MYSQL_VERSION_MAJOR} DESTINATION lib) INSTALL(FILES ${CMAKE_CURRENT_SOURCE_DIR}/audit_plugin.so DESTINATION lib/plugin) EOFStep 4修改spec文件核心vim ~/rpmbuild/SPECS/mysql-community-server.spec # 修改以下字段 Name: mysql-community-server Version: 8.0.32 # 降级版本 Release: 1.1.myorg # 内部发布号 Source0: mysql-%{version}.tar.gz Source1: audit_plugin.so Patch1001: add-audit-plugin.patch ... %prep %setup -n mysql-%{version} %patch1001 -p1 # 应用补丁 # 在%build段末尾添加 install -m 0755 %{SOURCE1} %{buildroot}%{_libdir}/mysql/plugin/ ... %files %{_libdir}/mysql/plugin/audit_plugin.so # 声明新文件 # 其他%files保持不变Step 5构建与验证# 清理旧构建可选 rm -rf ~/rpmbuild/BUILDROOT/* # 执行构建 rpmbuild -ba --define _topdir %(pwd)/rpmbuild ~/rpmbuild/SPECS/mysql-community-server.spec # 成功后新包在 ~/rpmbuild/RPMS/x86_64/mysql-community-server-8.0.32-1.1.myorg.el9.x86_64.rpm # 验证 rpm -qpi ~/rpmbuild/RPMS/x86_64/mysql-community-server-8.0.32-1.1.myorg.el9.x86_64.rpm rpm -qpl ~/rpmbuild/RPMS/x86_64/mysql-community-server-8.0.32-1.1.myorg.el9.x86_64.rpm | grep audit rpm -Kv ~/rpmbuild/RPMS/x86_64/mysql-community-server-8.0.32-1.1.myorg.el9.x86_64.rpm整个过程耗时约8分钟网络下载源码占5分钟构建本身3分钟。关键点在于所有操作都在~/rpmbuild/目录下路径完全可控所有改动spec、patch、source都可git管理最终rpm包的Build Date是构建时的时间戳而非原始包的2023年确保dnf update能正确识别新版本。4.2 交叉编译repack如何把x86_64 RPM转成aarch64某些国产化项目要求同一套软件在x86_64和aarch64双平台运行。但上游只提供x86_64 RPM没有src.rpm。这时Binary-based repack结合交叉编译工具链是唯一出路。Step 1安装aarch64交叉编译工具链dnf install aarch64-linux-gnu-gcc aarch64-linux-gnu-gcc-c aarch64-linux-gnu-binutils # 创建交叉编译环境变量 export CCaarch64-linux-gnu-gcc export CXXaarch64-linux-gnu-g export ARaarch64-linux-gnu-ar export STRIPaarch64-linux-gnu-stripStep 2解包并识别可执行文件rpm2cpio original-x86_64.rpm | cpio -idmv # 找出所有需要重编译的二进制 find . -type f -executable -exec file {} \; | grep ELF 64-bit LSB pie executable, x86-64 # 假设找到 ./usr/bin/myappStep 3获取源码并交叉编译# 从myapp官网下载源码或用rpm -qip original.rpm 查Source RPM名再dnf download --source tar -xf myapp-1.0.tar.gz cd myapp-1.0 ./configure --hostaarch64-linux-gnu --prefix/usr make -j$(nproc) make DESTDIR$(pwd)/install-root install # 此时 install-root/usr/bin/myapp 是aarch64二进制Step 4构造新spec并构建# 复制原始spec修改Architectures: BuildArch: aarch64 %define _arch aarch64 # %install段改为 rm -rf %{buildroot} cp -r install-root/* %{buildroot}/ # %files段保持不变但%attr权限需确认 # 最后rpmbuild -ba --target aarch64 myapp.spec难点在于交叉编译时configure脚本可能硬编码x86指令集。解决方案是在%configure前加sed -i s/-marchx86-64/-marcharmv8-a/g configure。我为某AI推理框架做过此操作原始x86_64 RPM里有libinference.so用aarch64-linux-gnu-gcc -shared -fPIC重新编译后file libinference.so显示ELF 64-bit LSB shared object, ARM aarch64, version 1 (SYSV), dynamically linked完美适配飞腾CPU。4.3 自动化脚本用shell封装重复操作高频repack必须脚本化。以下是我日常使用的repack.sh核心逻辑已脱敏#!/bin/bash # Usage: ./repack.sh original.rpm new_version new_release patch_file set -e ORIGINAL_RPM$1 NEW_VERSION$2 NEW_RELEASE$3 PATCH_FILE$4 # 1. 初始化 rpmdev-setuptree SPEC_DIR~/rpmbuild/SPECS SOURCE_DIR~/rpmbuild/SOURCES # 2. 获取src.rpm并解包 SRC_RPM$(rpm -qpis $ORIGINAL_RPM | grep Source RPM | awk {print $4}) if [ -z $SRC_RPM ]; then echo Error: No Source RPM found in $ORIGINAL_RPM exit 1 fi dnf download --source $SRC_RPM rpm -i ${SRC_RPM##*/} # 3. 替换源码 TARBALL$(rpm -qpi $ORIGINAL_RPM | grep Source | awk {print $3} | sed s/\.src\.rpm$/.tar.gz/) wget https://example.com/sources/$TARBALL -O $SOURCE_DIR/$TARBALL # 4. 应用补丁 cp $PATCH_FILE $SOURCE_DIR/ SPEC_NAME$(basename $SPEC_DIR/*.spec) sed -i s/Version:.*/Version: $NEW_VERSION/ $SPEC_DIR