简介面向需要在Ubuntu 20.04无网或弱网环境中安装GCC的开发者、运维人员及嵌入式工程师。资源将编译器本体与所需依赖统一打包为31个文件包含30个deb软件包和1个自动安装脚本整体仅29.83MB便于通过U盘等介质拷贝到目标主机直接部署。包内覆盖build-essential、libmpc3、libisl22等核心依赖以及gcc-9、gcc-10基础包和binutils、libc-dev等配套组件基本满足C/C编译环境的完整依赖要求。安装脚本已按依赖顺序设计可自动完成dpkg安装减少手动处理依赖报错与安装冲突的困扰安装完成后还可通过gcc --version快速确认版本。目前已有8674人学习下载适合在隔离网络、内网服务器或离线生产环境中快速搭建编译工具链。1. Ubuntu 20.04 离线装 gcc一份 zip 包的背后是整棵依赖树一台 Ubuntu 20.04 的机器没有外网却急着要一个能用的 gcc 编译器而你手里只有别人递过来的一份 gcc.zip。这个场景我在内网项目里碰到过不止一次生产区段、涉密环境、离线机房都是这种需求的常客。gcc 不像单个小工具它后面拖着一整棵依赖树——cpp、binutils、libc6-dev、libgcc-9-dev缺一个都编译不了这也是“ubuntu安装gcc失败”最常见的原因。这篇文章按 ubuntu20.04 离线安装 gcc 的实际操作顺序来写zip 包里到底该有什么、按什么顺序装、依赖怎么补、装完怎么验证、翻车了从哪查。新手能照着一步步做完熟手可以重点看避坑和最后的本地源方案。2. 动手前先确认三件事架构、系统版本和依赖关系离线安装最怕的不是不会敲命令而是拿错包。gcc 的 zip 包不是随便从网上拖一个就能用它必须和当前机器的 CPU 架构、Ubuntu 版本、glibc 版本匹配。这一章把装之前必须搞清楚的三件事讲透顺序不要跳。2.1 用三条命令确认目标机器的架构与系统版本常见做法是在打包机上先跑一遍这三条命令确认目标机器和打包机是同一套环境。如果 zip 是同事给的也要先验证它确实适用于当前机器。cat /etc/os-release lsb_release -a uname -m第一条命令看发行版信息输出里的VERSION_ID必须能看到20.04字样VERSION_CODENAME是focal。第二条lsb_release -a显示的Distributor ID和Release更直观但某些精简系统没装 lsb-release所以我习惯先跑cat /etc/os-release。第三条uname -m最关键它决定你该找amd64还是arm64的 deb 包x86 服务器输出x86_64鲲鹏、飞腾等国产平台输出aarch64对应装arm64架构的包。这三条命令在打包机和目标机上都要跑两个输出不一致zip 包基本可以扔了。2.2 gcc 的依赖树为什么 gcc 不能像单个 .deb 那样一装就完事很多人以为dpkg -i gcc_xxx.deb就完事了结果报一屏依赖错误。原因是 Ubuntu 20.04 里的 gcc 是个元包meta package真正干活的编译器本体在 gcc-9 里元包只是把一串包捆绑在一起。它的依赖关系大致长这样gcc (元包) ├── gcc-9 │ ├── cpp-9 │ ├── binutils │ ├── libgcc-9-dev │ │ ├── libc6-dev │ │ │ ├── libc6 │ │ │ └── linux-libc-dev │ │ └── libgcc-s1 │ └── libcc1-0 └── gcc-9-base这棵树的深度比你想象得深。libc6-dev本身还依赖linux-libc-dev内核头文件而binutils又依赖libctf-nobfd0这类运行时库。所以一份完整的离线 gcc.zip少说也要包含 10 到 15 个 .deb 文件而不是只装一个 gcc 包就能完事。判断一份 zip 包是否完整最直接的办法是在打包机上用apt-cache depends gcc把依赖清单拉出来然后核对手里的 deb 文件列表。缺了任何一层目标机上 dpkg 都会用依赖错误来提示你。2.3 三条离线路径怎么选zip 包、apt 缓存包、预编译二进制离线装 gcc 大体有三条路我按实际项目里见过的场景整理成下面这张表。三条路各有适用边界别指望一条路走天下。路径典型场景优点缺点用 apt download 打包成 zip批量分发、环境可控安装后与在线 apt 管理一致卸载、升级都干净收集依赖费时容易漏包直接解压预编译二进制只要 gcc 能用、不想动系统不影响 dpkg 状态删目录即卸载PATH、库路径都要自己配可能缺运行库逐个 dpkg 安装零散 deb包少、依赖浅直观、可控依赖顺序错了会连环报错先说最常见的路径一在一台能联网且和目标机同版本、同架构的机器上用apt-get install --download-only或apt download把 gcc 和全部依赖拉下来然后 zip 打包。这条路径装完的 gcc 与在线安装完全等价后续apt-get upgrade也能正常接管。路径二适合不想污染系统的场景比如目标机上的 dpkg 数据库被业务系统锁死或者只是临时要编译个内核模块。路径三其实是路径一的残缺版只适合依赖极少的场景gcc 这种深度依赖树不推荐。3. 从 gcc.zip 到 gcc 可用完整安装步骤与 dpkg 参数说明拿到 zip 包之后真正的操作从解压开始。这一章按顺序写完整流程每一步都给了命令也解释了为什么这么做。在离线机器上动手时建议按这个顺序一步步走不要跳。3.1 解压 zip 包并检查目录结构文件清单对了再动手先把 zip 包解压到工作目录然后看清单。这一步虽然简单但能提前发现一半的翻车问题。mkdir -p ~/gcc-offline unzip gcc.zip -d ~/gcc-offline ls -lh ~/gcc-offlineunzip的-d参数指定解压目标目录避免文件散落在当前目录。解压后先别急着安装花十秒钟看一下ls -lh的输出文件后缀必须是.deb数量应该在十个以上大小加起来在 50MB 到 150MB 之间具体取决于是否包含 gcc-9、g、libc6-dev 全套。如果你看到的是 gcc 的可执行文件和一批 .so 库文件而不是 .deb 包说明这份 zip 是预编译二进制那要跳到后面的 PATH 配置部分处理不能用 dpkg 装。3.2 按依赖顺序安装 .debdpkg -i 的三种用法确认是 .deb 集合后第一反应是sudo dpkg -i *.deb一把梭。这样不是不行但可能一上来就报一堆依赖错误看着吓人。我更推荐按依赖深度分批安装心理有底。sudo dpkg -i ~/gcc-offline/linux-libc-dev*.deb sudo dpkg -i ~/gcc-offline/libc6-dev*.deb sudo dpkg -i ~/gcc-offline/binutils*.deb ~/gcc-offline/libgcc-9-dev*.deb sudo dpkg -i ~/gcc-offline/cpp-9*.deb ~/gcc-offline/gcc-9*.deb sudo dpkg -i ~/gcc-offline/gcc_*.deb ~/gcc-offline/g_*.deb这五条命令的顺序就是依赖树的层次最底层的内核头文件先装然后是 libc 开发头文件、汇编器和库、预处理器、编译器本体。*.deb是 shell 通配符会展开成所有匹配文件名比如gcc-9*.deb会匹配gcc-9_9.4.0-1ubuntu1~20.04.2_amd64.deb这种名字。如果 zip 包里的文件命名不规则比如版本号带号通配符仍然有效但建议先用ls确认文件名再执行。如果你不确定包的依赖顺序也有个偷懒但不怎么好看的办法对全部 deb 执行一次dpkg -i然后立刻再执行一遍。sudo dpkg -i ~/gcc-offline/*.deb sudo dpkg -i ~/gcc-offline/*.deb为什么执行两遍第一遍时 dpkg 会把所有包解包依赖关系不满足的包会被标记为“未配置”并报错但已经解开的包文件会留在系统里第二遍时底层依赖已经就位dpkg 会补齐配置阶段多数情况就能成功。如果两遍之后仍有报错的包用下面的命令尝试恢复sudo dpkg --configure -adpkg --configure -a会把所有处于“已解包未配置”状态的包重新配置一遍。在安装过程中途被 CtrlC 打断、或者断电之后这条命令是标准的后悔药。3.3 依赖缺失怎么办包内自补与包外补包的边界离线环境最尴尬的情况是dpkg 报缺某个包但 zip 里没有。这时候先冷静区分一种情况——缺的包到底在不在 zip 里。如果 zip 里有这个 deb 文件只是刚才的通配符没匹配到比如 deb 名里带了架构后缀之外的特殊字符直接指定文件名重装即可。如果 zip 里没有且目标机完全断网就只能去打包机上补。常见做法是回到那台能联网的同版本机器上执行apt-get download 缺失包名apt-get download只下载不安装下载到的 .deb 与apt-get install拿到的完全一致。比如缺libisl22就在打包机上执行apt-get download libisl22把得到的 deb 加进 zip 里拷过去。要注意的是带着依赖关系的包得顺着依赖链一起下别只下一个包又缺另一个。如果目标机只是短暂能联网也可以试着用sudo apt-get update sudo apt-get -f installapt-get -f install的作用是修复系统中不完整的依赖关系会自动从软件源拉缺失包。但这是联网路径内网环境下大概率失败失败信息里会列出它尝试访问的源地址也能帮你反向确认到底是哪些包缺失然后就近找一台同版本机器打包过来。4. 避坑指南离线装 gcc 的 5 个高频翻车点与解决办法离线装 gcc 的坑很多不是技术深度问题而是前提条件没对齐。以下 5 条是我实际碰到过、也在群里看别人反复踩的典型场景每条都按“现象 → 原因 → 解决”来写。4.1 架构不匹配报错里出现 architecture does not match现象执行dpkg -i时直接报错类似package architecture (amd64) does not match system (arm64)包一个都装不进去。原因打包机是 x86 服务器目标机是 arm64 架构或者反过来。zip 包在选择时就拿错了。这个错误提示其实已经把原因说得很直白但很多人看到一屏红字就慌了。解决回到打包机确认uname -m的输出与目标机一致重新生成 zip 包。在 aarch64 机器上deb 包后缀是_arm64.deb不是_amd64.deb。顺手看一眼file命令的输出也能确认架构。这里没有捷径架构不匹配的包不能硬装硬装只会把 dpkg 数据库搞乱。4.2 依赖顺序错乱gcc-9 depends on cpp-9 连环报错现象先装了 gcc-9然后 dpkg 报gcc-9 depends on cpp-9; however: Package cpp-9 is not installed再试dpkg --configure -a也没用。原因安装顺序颠倒了或者 zip 是半份的只有上层包没有下层依赖。这种情况在只拷贝了 gcc 主包、没带cpp-9和binutils时尤其常见。解决先执行第 3 章的按层安装顺序从linux-libc-dev和libc6-dev开始。如果 zip 里确实没有 cpp-9 的 deb去打包机补。另一个通用做法是dpkg -i *.deb连着执行两次第一次把所有包解包并暴露缺失项第二次把依赖补齐。注意观察两次报错列表的差异——如果第二次的报错变了说明依赖在推进如果两次报错一模一样说明缺的包压根不在 zip 里。4.3 装完还是旧版本gcc 版本号不对或命令找不到现象好不容易装完敲gcc --version显示的版本还是系统最老的 gcc或者干脆报command not found。有同学在“gcc升级后为啥还是旧版本”这类问题里折腾很久。原因Ubuntu 20.04 的/usr/bin/gcc其实是个软链接默认指向/usr/bin/gcc-9。如果你装了新版的 gcc-10 或 gcc-12但/usr/bin/gcc仍然指向 gcc-9或者 PATH 里有其他目录的 gcc 排在前面旧版本就会被优先调用。解决先查清当前状态which -a gcc ls -l /usr/bin/gccwhich -a会列出 PATH 里能找到的所有 gcc 路径ls -l能看出/usr/bin/gcc这个软链接指向谁。确认新装的版本路径后用 update-alternatives 调整优先级第 6 章会写完整命令或者直接重建软链接sudo ln -sf /usr/bin/gcc-9 /usr/bin/gcc。注意直接用软链接会在 dpkg 重新配置时被覆盖长期来看还是 update-alternatives 正规。4.4 跨发行版版本错配运行时报 GLIBC_2.34 not found现象gcc 装好了用它编译的 hello world 也能生成但一运行就报/lib/x86_64-linux-gnu/libc.so.6: version GLIBC_2.34 not found。原因这是把更新的发行版比如 Ubuntu 22.04 或 Debian 12里的 gcc/libc6-dev 包装到了 20.04 上。新版本的 gcc 编译出来的二进制默认链接了新 glibc 才有的符号而 20.04 的 glibc 版本是 2.31没有这些符号。这种错配在离线环境里非常隐蔽因为安装时报依赖错误可能不明显问题要到编译产物运行才暴露。解决严格执行“同版本系统打包”原则。zip 包必须在同样lsb_release -r输出为 20.04 的机器上生成不能拿手头任意版本的 deb 混用。如果已经在错配状态最干净的办法是把装进去的高版本 libc6-dev 和 gcc 全部卸载回退到 focal 源里的版本。需要检查编译产物链接了哪些 glibc 版本时可以用strings /lib/x86_64-linux-gnu/libc.so.6 | grep GLIBC_看系统支持的最大版本号。4.5 zip 解压后权限异常Permission denied 或 gcc 无法执行现象dpkg 安装一切正常但执行 gcc 时提示Permission denied或者 zip 解压出来根本不是 deb 包而是二进制目录里面的文件全是-rw-r--r--没有可执行位。原因zip 文件在 Windows 或 macOS 上被解压后重新打包过Unix 权限位尤其是可执行位丢失。deb 包本身不依赖外部可执行位dpkg 会按包内记录的权限恢复所以 deb 路径下这问题少见但如果是预编译二进制的 zip可执行位丢了gcc 就是跑不起来。解决确认 zip 内容的实际形态。如果是预编译二进制解压后执行find /opt/gcc/bin -type f -exec chmod x {} \;这条命令把/opt/gcc/bin下所有文件加上可执行权限。对 .so 库文件不需要chmod x但有也不碍事。另外一个预防措施是在内网传输 deb 文件时优先用 tar 而不是 ziptar czf和tar xzf能完整保留 Unix 权限位和属主信息不会出这种玄学问题。5. 装完不是终点验证 gcc 可用性与编译链路的三个步骤很多人在dpkg -i全部成功后就以为完工了结果真正编译项目时才发现头文件路径不对或者链接器缺库。这一章讲装完后的验证三步走每一步都有明确的通过标准。5.1 版本与配置验证gcc --version 之外还要看什么最基本的验证是版本号但光看版本号不够。我一般同时跑这两条命令gcc --version gcc -v 21 | grep -E gcc version|Target|Thread modelgcc --version输出gcc version 9.4.0 (Ubuntu 9.4.0-1ubuntu1~20.04.2)确认安装成功。gcc -v的输出更长第二行就是Target: x86_64-linux-gnu确认目标架构后面还有Thread model: posix确认线程模型是 POSIX。这两项如果和预期不符说明安装的包来源不对——比如在 x86_64 机器上装了 i386 的交叉编译包版本号能显示但编译出来的程序跑不了。如果gcc -v在最后几行显示COLLECT_GCC/usr/bin/gcc-9说明当前生效的确实是 gcc-9。如果这里指向的路径和which gcc不一致就要回看 4.3 里的软链接问题。5.2 用最小 C 程序验证编译、链接与动态库依赖版本号正常不代表编译链路通。写一个最小程序把编译、链接、运行、动态库依赖全部验证一遍。#include stdio.h int main(void) { printf(gcc %d.%d.%d works offline\n, __GNUC__, __GNUC_MINOR__, __GNUC_PATCHLEVEL__); return 0; }这段代码有两点用心__GNUC__等三个宏是编译器在编译期展开的直接输出当前 gcc 的实际版本不需要在命令行手动敲版本号printf会强制链接器把 libc 链进来验证 libc6-dev 装得对不对。编译运行gcc -Wall -O2 -o hello hello.c ./hello ldd hello-Wall打开所有常规警告-O2是常用优化等级-o hello指定输出文件名。编译成功后./hello输出版本信息。最后一条ldd hello输出的第一行应该是libc.so.6 /lib/x86_64-linux-gnu/libc.so.6。如果这里出现not found说明你的 gcc 链接到了非系统的库目录且这个库没被加载如果直接报GLIBC_2.34 not found之类的运行时错误对应 4.4 的版本错配问题回炉重装吧。5.3 让 gcc 常驻 PATH环境变量配置的两种写法如果你安装的是 deb 包gcc 自动落在/usr/bin不需要配 PATH。但如果是预编译二进制解压在/opt/gcc或者你把 gcc 装到了非标准目录就必须把 PATH 指过去否则每次都要输入全路径。sudo tee /etc/profile.d/gcc-offline.sh EOF export PATH/opt/gcc/bin:$PATH export LD_LIBRARY_PATH/opt/gcc/lib:$LD_LIBRARY_PATH EOF source /etc/profile.d/gcc-offline.shtee配合 here-doc 把两行 export 写进/etc/profile.d/gcc-offline.sh这是系统级环境变量配置所有用户登录时都会自动加载避免只改当前 shell 的.bashrc导致换一个终端就失效。LD_LIBRARY_PATH只在 gcc 自带独立库目录比如/opt/gcc/lib64时需要如果 gcc 只依赖系统 glibc可以不加。验证方式新开一个终端敲which gcc输出的路径应该是/opt/gcc/bin/gcc。6. 离线环境里更进一步的玩法多版本 gcc 切换与本地软件源离线环境有个长期痛点今天要装 gcc 9 编译老项目明天要装 gcc 10 跑新特性每次都要翻 zip 包重装太痛苦。这里给出两个解决思路一个是用 update-alternatives 优雅切换多版本另一个是把离线包整理成本地 apt 源让apt-get install在无网状态下也能直接干活。6.1 用 update-alternatives 管理离线机器上的多个 gcc 版本如果你通过离线方式先后装了 gcc-9 和 gcc-10可以用 update-alternatives 接管/usr/bin/gcc的软链接sudo update-alternatives --install /usr/bin/gcc gcc /usr/bin/gcc-9 100 sudo update-alternatives --install /usr/bin/gcc gcc /usr/bin/gcc-10 90 sudo update-alternatives --config gcc--install的第三个参数是实际二进制路径第四个参数gcc是这个 alternative 组的名称最后一个数字是优先级数值大的默认生效。这里把 gcc-9 设为 100、gcc-10 设为 90所以默认版本是 gcc-9执行--config gcc后会出现交互菜单输入序号即可临时切换。同样的方法可以给g、cc各做一组。这个方案的额外好处是/usr/bin/gcc始终是合法软链接dpkg 的完整性检查不会报警。6.2 用 dpkg-scanpackages 建一个本地离线软件仓库比翻 zip 包更省事的做法是把所有离线 deb 整理成一个本地仓库目录让 apt 直接从这个目录安装还能自动解决依赖不用再手动排队 dpkg。apt-get install -y dpkg-dev mkdir -p /opt/local-repo cp ~/gcc-offline/*.deb /opt/local-repo/ cd /opt/local-repo dpkg-scanpackages . /dev/null | gzip -9c Packages.gz echo deb [trustedyes] file:///opt/local-repo ./ | sudo tee /etc/apt/sources.list.d/local-repo.list sudo apt-get update sudo apt-get install -y gccdpkg-scanpackages扫描目录下所有 deb 并生成 Packages 索引文件这是 apt 识别可用包和依赖关系的依据。gzip -9c把索引压缩成 Packages.gzapt 会优先读取压缩版本。最后写入的 sources.list 条目用的是file://协议指向本地目录[trustedyes]跳过签名校验因为离线仓库没有密钥签名。执行完apt-get update后apt-get install gcc就能自动解析依赖缺哪个从仓库里补。如果目标机器上没有 dpkg-dev 工具可以在打包机上把 Packages.gz 生成好连同 deb 文件一起拷过去效果一样。这套本地仓库方案我后来在好几个离线项目里都用了比每次传 zip 再手动 dpkg 省心太多。吃亏的地方是第一次搭仓库时忘了把dpkg-dev也拷进去到现场才发现生成不了索引还得折返一趟取包。后来我都是直接把 dpkg-dev、apt-utils 这类辅助包无条件塞进离线包清单。版本管理也一样提前在 update-alternatives 里注册好所有已装版本别等到两边项目切换时才临时翻包。如果你的离线机器不止一两台这套“zip 打底 本地源收尾”的组合基本能把 gcc 这块彻底放下希望帮到你。本文还有配套的精品资源点击获取