Ubuntu 18.04 下用 dpkg 安装 gcc 7 并配置多版本共存指南 简介面向 Ubuntu 18.04 系统用户与运维开发者的 GCC 7 离线安装包针对无外网或受限网络环境下编译工具链缺失、在线 apt 安装失败等问题提供可直接落地的本地部署方案。压缩包共 25 个文件以 24 个 deb 安装包和 1 个 sh 脚本为主整体约 20.05MBdeb 覆盖 gcc-7、cpp-7、binutils 及 libasan4、libgomp1、libmpc3 等编译运行依赖sh 脚本用于批量安装与顺序处理避免手动逐个 dpkg 的繁琐。已有 3207 人学习下载说明该离线方案在同类场景中具备较高参考价值。读者可据此在目标机器上快速补齐 GCC 7 编译环境减少依赖缺失与版本冲突带来的排错成本适合需要稳定构建 C/C 项目的开发与维护人员使用。1. 从一台编译报错的 Ubuntu 18.04 说起上周帮同事救一台 Ubuntu 18.04 的构建机现象很典型make跑到一半报cc1plus: error: unrecognized command line option -stdc17查了半天发现系统自带的 gcc 是 7.5.0但项目里某个第三方库的 CMake 脚本硬性要求 gcc 7 以上且带完整 C17 支持而机器上之前被人手动装过一版来路不明的 gccupdate-alternatives指向混乱gcc -v和/usr/bin/gcc根本不是同一个东西。这类问题在 Ubuntu 18.04 上太常见了——系统默认源里的 gcc 版本停在 7.x很多新项目又刚好卡在这个版本线上装对了能跑装歪了就是一堆玄学链接错误。这篇记录的就是我在这类机器上反复走过的一套流程用gcc7 ubuntu18.04 dpkg这套组合把编译器环境装干净、配正确、验证到位。适合两类人一是刚接手一台 Ubuntu 18.04 构建机、需要把 gcc 7 装利索的新手二是被多版本 gcc 共存搞烦了、想搞清楚dpkg安装和update-alternatives切换逻辑的熟手。装不好会怎样轻则编译报错重则把系统自带的 gcc 覆盖掉连apt都跑不起来那就真得进恢复模式救了。2. 装前把环境盘清楚版本、依赖与 dpkg 底账动手之前先别急着dpkg -iUbuntu 18.04 这套环境有几个前置条件必须先确认否则装到一半发现依赖缺失或者装完发现和系统自带 gcc 打架返工成本比预检高得多。2.1 确认系统版本与现有 gcc 状态第一步永远是看清楚脚下踩的是什么。Ubuntu 18.04 的代号是 bioniclsb_release和uname两个命令交叉验证避免在容器或裁剪镜像里误判。# 确认发行版和内核bionic 对应 Ubuntu 18.04 lsb_release -a uname -r # 查当前 gcc / g 的真实版本和路径 gcc --version g --version which gcc g readlink -f $(which gcc) # 看系统里已经装了哪些 gcc 相关包 dpkg -l | grep -E ^ii\s(gcc|g\\|build-essential)readlink -f这一步很关键因为/usr/bin/gcc往往是个软链指向/etc/alternatives/gcc再指向真正的/usr/bin/gcc-7。如果只跑gcc --version看到 7.5.0 就以为万事大吉很可能实际编译时用的是另一个路径下的旧版本。dpkg -l的输出里ii表示已安装且配置完成如果看到iU已解包未配置或rc已删除保留配置说明之前有人装到一半中断过这种残留必须先清掉再装新的。我一般还会顺手看一眼apt源里 gcc-7 的可用版本确认和要装的包版本对得上# 查源里 gcc-7 相关包的可用版本 apt-cache policy gcc-7 g-7如果apt-cache policy显示候选版本是 7.5.0而手上的 zip 包里是 7.4.0 或别的构建就要想清楚是走 apt 装还是走 dpkg 装本地包——这两条路后面会分别讲混着来最容易出问题。2.2 依赖清单dpkg 装包前必须补齐的几样东西dpkg和apt最大的区别是dpkg只负责把包解开、跑安装脚本不帮你解决依赖。所以用dpkg -i装 gcc 7 的 deb 包之前得先把依赖手动补齐否则会卡在dpkg: dependency problems prevent configuration。常见做法是先装好构建基础链和 gcc 7 运行时依赖# 补齐 dpkg 安装 gcc-7 所需的基础依赖 sudo apt-get update sudo apt-get install -y build-essential libc6-dev libgcc-7-dev \ libstdc-7-dev binutils makebuild-essential是个元包拉进来gcc、g、make、libc6-dev一整套libgcc-7-dev和libstdc-7-dev是 gcc 7 编译 C 时链接阶段要用的开发库缺了会在链接期报cannot find -lstdc。binutils提供ld、as这些底层工具make不用多说。这几样装完dpkg -i装 gcc 7 的 deb 包时基本不会再因为依赖报错。提示如果机器上已经有别的 gcc 版本比如 gcc-8 或 gcc-9libgcc-7-dev和libstdc-7-dev仍然要单独装不同大版本的运行时库是分开的不能互相替代。2.3 版本选择apt 源装还是 dpkg 装本地包这是整篇里最需要想清楚的一个取舍。Ubuntu 18.04 官方源里gcc-7的版本是 7.5.0如果你的 zip 包里提供的 deb 包正好也是 7.5.0 或兼容的 7.x两条路都行但如果 zip 包里是特定构建比如带补丁的 7.4.0就只能走dpkg。方式适用场景优点风险apt install gcc-7源里有对应版本追求省事自动解决依赖可apt统一管理版本被源锁定无法装特定构建dpkg -i *.deb手上有特定 deb 包需精确控制版本版本完全可控离线可装依赖要手动补装错难回滚我一般的判断标准是只要源里版本能满足项目要求优先apt因为后续升级、卸载都干净只有当项目明确要求某个特定构建、或者机器完全离线时才走dpkg装本地包。这篇后面两条路都会写到因为实际运维里两种场景都躲不掉。3. 两条安装路径apt 装 gcc-7 与 dpkg 装本地 deb环境盘清楚之后安装本身其实不复杂复杂的是装完之后多版本怎么共存、默认指向怎么切。这一章把两条安装路径拆开写每条都给到能直接抄的命令和参数说明。3.1 走 apt 源安装 gcc-7 与 g-7源里能解决的情况这是最省心的方式。Ubuntu 18.04 默认源就带 gcc-7直接装即可# 从官方源安装 gcc-7 和 g-7 sudo apt-get update sudo apt-get install -y gcc-7 g-7 # 验证装上的版本 gcc-7 --version g-7 --version装完之后系统里会多出/usr/bin/gcc-7和/usr/bin/g-7两个可执行文件但注意此时gcc这个命令仍然指向系统原来的版本gcc-7只是并存。这是 Ubuntu 多版本管理的设计不会自动改默认指向需要手动用update-alternatives切下一节讲。apt-get install的-y是自动确认批量部署脚本里必加手动操作时可以不加看一眼要装哪些包再确认避免误装一堆推荐包。如果只想装最小集合可以加--no-install-recommends# 只装必需包不拉推荐包适合精简镜像 sudo apt-get install -y --no-install-recommends gcc-7 g-73.2 用 dpkg 安装 zip 包里的本地 deb离线机器或者需要特定构建时走这条路。假设 zip 包解压后目录里有若干.deb文件先看清楚再装# 解压 zip 包并进入目录 unzip ubuntu18.04gcc.zip -d gcc7-pkgs cd gcc7-pkgs # 列出包内 deb 文件确认包名和架构 ls -lh *.deb dpkg-deb -I gcc-7_*.deb | head -20dpkg-deb -I看的是包的元信息重点看Architecture是不是amd64、Depends里列了哪些依赖。架构不对比如拿到 i386 的包装上去也跑不起来Depends里的包如果没装dpkg -i会报依赖错误。确认无误后安装注意一次把所有 deb 传给dpkg -i让它自己按顺序处理# 一次性安装目录下所有 debdpkg 会按依赖顺序处理 sudo dpkg -i *.deb # 如果报依赖错误用 apt 补依赖apt 能读 dpkg 的未完成状态 sudo apt-get install -f -ydpkg -i *.deb一次传多个包比一个个装靠谱因为 dpkg 会在这一批里做依赖排序。装完如果提示dependency problems别慌跑apt-get install -f让它把缺的依赖补上这个-f是--fix-broken的缩写专门修 dpkg 留下的半配置状态。注意dpkg -i装本地包时如果包版本比源里的低dpkg 不会拦你但后续apt upgrade可能把它又升回去。要锁版本得用apt-mark hold这个后面避坑章节会讲。3.3 多版本共存update-alternatives 的配置逻辑不管走哪条路装完系统里都会出现多个 gcc 版本并存。Ubuntu 用update-alternatives管理这类「同一命令多个实现」的场景理解它的优先级机制比死记命令重要。先看当前 alternatives 状态# 查看 gcc 的 alternatives 配置和当前指向 update-alternatives --display gcc update-alternatives --display g输出里会列出所有候选路径、优先级priority和当前选中的那个。优先级数字大的在--auto模式下优先但手动--config选过之后会锁定为手动模式。把 gcc-7 注册进 alternatives# 把 gcc-7 注册为 gcc 的一个候选优先级设高一点 sudo update-alternatives --install /usr/bin/gcc gcc /usr/bin/gcc-7 70 \ --slave /usr/bin/g g /usr/bin/g-7 # 交互式选择默认版本 sudo update-alternatives --config gcc--install的四个参数分别是链接路径/usr/bin/gcc、alternatives 组名gcc、实际可执行文件路径/usr/bin/gcc-7、优先级70。--slave是把 g 绑到 gcc 这个组上切 gcc 时 g 跟着切避免出现 gcc 是 7 而 g 还是旧版本的错位——这个错位是后面编译报错的高发原因。--config会弹出交互菜单列出所有候选让你选编号。脚本化部署时用非交互方式# 非交互直接切到 gcc-7适合自动化脚本 sudo update-alternatives --set gcc /usr/bin/gcc-7--set直接指定路径不走菜单适合写进部署脚本。切完再跑一次gcc --version和g --version确认两者版本一致这一步别省。4. 避坑与排查gcc 7 装完最常见的五个翻车现场装 gcc 这件事命令本身没几个翻车基本都翻在装完之后。下面五条是我在不同机器上真实踩过的按「现象 → 原因 → 解决」记下来遇到对号入座就行。4.1 现象编译报cc1plus: error: unrecognized command line option -stdc17原因gcc已经切到 7但g还指向旧版本或者反过来。C 编译走的是g-stdc17是 g 7 才完整支持的选项旧版 g 不认。根因通常是update-alternatives只切了 gcc 没切 g或者--slave没绑对。解决先分别确认两个版本再重新绑定# 分别确认版本不一致就是问题所在 gcc --version | head -1 g --version | head -1 # 重新注册确保 g 作为 slave 跟随 gcc sudo update-alternatives --install /usr/bin/gcc gcc /usr/bin/gcc-7 70 \ --slave /usr/bin/g g /usr/bin/g-7 sudo update-alternatives --set gcc /usr/bin/gcc-74.2 现象dpkg -i报dependency problems prevent configuration原因dpkg不解决依赖zip 包里的 deb 依赖的libgcc-7-dev、libstdc-7-dev等没装。这在离线机器上尤其常见因为apt-get install -f也需要源可用。解决有网就跑sudo apt-get install -f -y自动补离线机器得提前把依赖 deb 一起放进 zip 包按依赖顺序手动装# 离线场景先装依赖包再装主包 sudo dpkg -i libgcc-7-dev_*.deb libstdc-7-dev_*.deb sudo dpkg -i gcc-7_*.deb g-7_*.deb4.3 现象装完apt命令本身报错提示 gcc 相关包损坏原因用dpkg -i强装了一个和系统已有 gcc 包冲突的版本把系统自带的gcc-7-base或libgcc1覆盖了导致apt依赖链断裂。这是最危险的一种因为apt坏了连修复工具都装不上。解决先看dpkg -l | grep -E gcc|libgcc找出状态异常的包iU、iF然后用dpkg --configure -a尝试重配不行就apt-get install --reinstall重装系统自带版本# 修复半配置状态的包 sudo dpkg --configure -a # 重装系统自带的 gcc 基础包恢复 apt 依赖链 sudo apt-get install --reinstall -y gcc-7-base libgcc14.4 现象update-alternatives --config gcc里看不到 gcc-7原因装是装上了但没注册进 alternatives或者注册时组名写错比如写成了gcc-7而不是gcc。组名是 alternatives 的索引键写错就等于注册到了另一个组--config gcc自然看不到。解决用--display确认组名重新用正确的组名注册# 看所有 alternatives 组确认 gcc 组下有哪些候选 update-alternatives --get-selections | grep gcc # 组名统一用 gcc重新注册 sudo update-alternatives --install /usr/bin/gcc gcc /usr/bin/gcc-7 704.5 现象apt upgrade之后 gcc 又变回旧版本原因dpkg装的本地包版本低于源里的候选版本apt upgrade按「升级到源里最新」的逻辑把它覆盖了。这是 dpkg 装本地包的固有风险不是配置错误。解决对需要锁定的包用apt-mark hold阻止自动升级# 锁定 gcc-7 相关包防止 apt upgrade 覆盖 sudo apt-mark hold gcc-7 g-7 libgcc-7-dev libstdc-7-dev # 需要升级时再解锁 sudo apt-mark unhold gcc-7 g-7提示apt-mark hold只挡apt upgrade挡不住手动dpkg -i覆盖。锁了版本之后装别的包时留意别把 gcc 相关依赖一起带升级了。5. 装完怎么验证从版本号到真实编译跑通装完不验证等于没装。我见过太多「gcc --version显示 7.5.0 就以为完事」的情况结果一编译就露馅。这一章给一套从浅到深的验证流程最后落到一个真实编译用例上跑通了才算这套环境真的立住了。5.1 三层验证版本、路径、编译行为第一层看版本第二层看路径一致性第三层看真实编译行为。三层都过才算干净。# 第一层版本号 gcc --version | head -1 g --version | head -1 # 第二层路径一致性三者应指向同一个实际文件 which gcc g readlink -f $(which gcc) readlink -f $(which g) # 第三层编译行为用 C17 特性做探针 echo int main(){ auto x 1; return x; } /tmp/probe.cpp g -stdc17 /tmp/probe.cpp -o /tmp/probe /tmp/probe echo C17 OK第二层的readlink -f是关键它把软链一路解到最终文件。如果gcc和g解出来的最终路径不在同一个目录、或者版本号后缀不一致比如一个是gcc-7一个是g-6说明 alternatives 绑定有问题回到 3.3 节重新绑。第三层用-stdc17编译一个最小程序能编过且能跑说明 g 7 的 C17 支持是完整的。这一步比看版本号可靠得多因为有些机器上版本号对但实际链接的libstdc是旧的编译能过、运行时报GLIBCXX_3.4.24 not found。5.2 用一个小项目做端到端编译验证单文件探针过了再拿一个带多文件、带链接的小项目跑一遍验证头文件搜索路径和链接路径都对。我一般用下面这个最小结构# 建一个两文件的小项目验证编译链接全链路 mkdir -p /tmp/gcc7-check cd /tmp/gcc7-check cat util.h EOF #pragma once int add(int a, int b); EOF cat util.cpp EOF #include util.h int add(int a, int b) { return a b; } EOF cat main.cpp EOF #include iostream #include util.h int main() { std::cout add(2, 3) std::endl; return 0; } EOF # 分步编译再链接暴露头文件和库路径问题 g -stdc17 -c util.cpp -o util.o g -stdc17 -c main.cpp -o main.o g util.o main.o -o app ./app分步编译的好处是能把「编译期头文件找不到」和「链接期符号找不到」分开定位。如果-c阶段就报util.h: No such file是头文件搜索路径问题如果-c过了但链接报undefined reference to add是目标文件没参与链接或者链接顺序问题。一步到位g *.cpp -o app虽然省事但出错时定位不到具体阶段。跑出5就说明这套 gcc 7 环境从编译到链接到运行全通了。最后再确认一下libstdc的版本避免运行时库错配# 确认运行时链接的 libstdc 版本 ldd ./app | grep stdc strings /usr/lib/x86_64-linux-gnu/libstdc.so.6 | grep GLIBCXX_3.4.2ldd看的是运行时实际加载的库路径strings看的是这个库里包含哪些 GLIBCXX 符号版本。gcc 7 对应的libstdc.so.6应该包含到GLIBCXX_3.4.24左右如果只到3.4.21说明运行时库还是旧的得检查libstdc-7-dev是不是真装上了。5.3 一个收尾习惯把验证写进部署脚本从那以后我每次装完 gcc不管多急都强制把 5.1 那三层验证写进部署脚本末尾跑不过就exit 1不让半成品环境流到下一环。血泪经验是gcc 这种基础工具装的时候省一步验证后面编译报错时就得花十倍时间往回查而且往往查不到根上最后只能重装。把验证前置是给自己留后悔药。# 部署脚本末尾的验证段跑不过直接失败 set -e gcc --version | grep -q 7\. || { echo gcc 版本不对; exit 1; } g --version | grep -q 7\. || { echo g 版本不对; exit 1; } [ $(readlink -f $(which gcc)) $(readlink -f $(which g)) ] \ || echo 警告gcc 与 g 路径不一致请检查 alternatives echo int main(){return 0;} | g -stdc17 -x c - -o /tmp/v /tmp/v \ echo gcc7 环境验证通过这段脚本里set -e让任何一步失败就中断grep -q 7\.做版本前缀匹配路径一致性用字符串比较兜底最后用管道把源码直接喂给 g 编译省去临时文件。写进 CI 或部署脚本后每次环境变更都会自动过一遍比人肉检查靠谱。希望这套流程帮到你少走几次重装系统的弯路。本文还有配套的精品资源点击获取