apt-get 命令详解:update、install、remove 与 apt 的区别
apt-get 是 Debian 和 Ubuntu 上安装、升级、卸载 .deb 软件包的命令行工具。最常用的组合是先 apt-get update 刷新软件包列表,再 apt-get install 安装;卸载用 remove(保留配置)或 purge(连配置一起删)。apt 与 apt-get 用的是同一套引擎,交互操作用 apt 更顺手,脚本里用 apt-get 更稳妥。
apt-get 是 Debian 系发行版(Debian、Ubuntu 及其衍生版)的软件包管理命令,属于 APT 工具集。它负责从软件源下载 .deb 包、自动处理依赖,再交给底层的 dpkg 完成安装和卸载。日常用法就三步:先刷新软件包列表,再安装或卸载。
sudo apt-get update # 刷新软件包列表,不会升级任何软件
sudo apt-get install -y nginx # 安装 nginx 及其依赖,-y 表示自动确认
sudo apt-get remove nginx # 卸载程序,/etc/nginx 下的配置保留
子命令对照表:update、upgrade、install、remove 各做什么
| 命令 | 作用 | 会装新包吗 | 会删包吗 | 典型场景 |
|---|---|---|---|---|
update |
从软件源下载最新的软件包列表,不升级任何软件 | 否 | 否 | 每次安装、升级之前 |
upgrade |
把已安装的包升级到列表里的新版本 | 否,需要新依赖的包会被“保留”不升级 | 否 | 日常打安全补丁 |
dist-upgrade |
在 upgrade 的基础上处理依赖变化,需要时装新包、删冲突的包 | 是 | 是 | 内核升级、依赖关系变化 |
install 包名 |
安装或升级指定的包及其依赖 | 是 | 很少,只在冲突时 | 装软件 |
install -f |
修复依赖关系不完整的系统(--fix-broken) |
是 | 可能 | dpkg 装包后缺依赖 |
install --reinstall 包名 |
重新安装同一版本 | 否 | 否 | 程序文件被误删或改坏 |
remove 包名 |
卸载,保留配置文件 | 否 | 是 | 暂时不用,以后可能再装 |
purge 包名 |
卸载,并删除配置文件 | 否 | 是 | 彻底不要了,或想恢复默认配置重装 |
autoremove |
删除当初作为依赖自动装上、现在已经没有包需要的软件 | 否 | 是 | 卸载软件之后、清理旧内核 |
download 包名 |
只把 .deb 下载到当前目录,不安装,不下载依赖 | 否 | 否 | 离线安装、查看包里有什么 |
clean / autoclean |
清空 /var/cache/apt/archives 里下载过的 .deb;autoclean 只删已经无法再下载的旧版本 |
否 | 否 | 释放磁盘空间 |
几个常用的全局参数:-y 自动回答 yes;-s 只模拟、不真正执行,用来预先看会发生什么;-q 减少输出,适合写进日志;--no-install-recommends 只装必需的依赖,不装“推荐”的附带软件,服务器上用它能少装不少东西。
upgrade 和 dist-upgrade 的区别
在 Ubuntu 上执行 apt-get upgrade,经常看到这样的输出:
The following packages have been kept back:
linux-generic linux-headers-generic linux-image-generic
0 upgraded, 0 newly installed, 0 to remove and 3 not upgraded.
原因是新内核要以一个新的包名(包名里带着新的版本号)安装进来,而 upgrade 的规则是绝不装新包、绝不删包,只能把这几个元包“保留”在原版本。dist-upgrade 没有这个限制,会把新内核装上;直接 apt-get install linux-generic 也能达到同样的效果。Ubuntu 还有分阶段推送(phased updates)机制,部分更新会先推给一部分机器,没轮到的机器上这些包也会显示为暂不升级,过几天自然会升级,不必处理。
因为 dist-upgrade 会删包,执行前先模拟一遍,看看要删的是什么:
apt-get -s dist-upgrade | grep '^Remv' # 模拟执行,只列出将被删除的包
另一个常见误解:名字里有 dist,但 dist-upgrade 不会把 Ubuntu 22.04 升到 24.04,它只在当前软件源的范围内升级。跨大版本升级,Ubuntu 用 do-release-upgrade,Debian 按对应版本发行说明里的步骤先改软件源再升级。
apt 和 apt-get 的区别
apt 是 APT 1.0(2014 年)起加入的命令,把 apt-get 和 apt-cache 里最常用的功能合到一起,并改进了交互体验。两者底层是同一个库、同一份软件源配置、同一个下载缓存和同一个 dpkg 数据库:用 apt 装的包,可以用 apt-get 卸载,反过来也一样。
| 对比项 | apt | apt-get |
|---|---|---|
| 面向谁 | 在终端里手动操作的人 | 脚本和自动化工具 |
| 输出 | 有进度条和颜色,update 后提示有多少包可以升级 | 纯文本,格式多年保持稳定 |
| 在脚本里使用 | 输出被重定向时会打印 WARNING: apt does not have a stable CLI interface. Use with caution in scripts. |
没有这类警告 |
| upgrade 的行为 | apt upgrade 需要时会安装新的依赖包,但仍然不删包 |
apt-get upgrade 不装新包 |
| 下载的 .deb | 默认在安装成功后删除 | 保留在 /var/cache/apt/archives |
| 查询 | apt search、apt show、apt list --installed、apt list --upgradable |
查询要用 apt-cache search、apt-cache show、apt-cache policy |
| 对应关系 | apt full-upgrade |
apt-get dist-upgrade |
选择很简单:自己登录上去装个软件、看看有什么可升级,用 apt;写进初始化脚本、定时任务、自动化工具里的,用 apt-get,它的参数和输出格式不会随版本变来变去,脚本解析起来更可靠。
安装软件:指定版本、本地 .deb 与 install -f
先看清楚要装的是哪个版本、来自哪个源:
apt-cache policy nginx # Installed 是已装版本,Candidate 是将要安装的版本,下面列出各个来源
apt-cache madison nginx # 列出软件源里所有可装的版本
需要固定版本时,在包名后面加 =版本号,版本号照 madison 输出的第二列原样填写,然后锁定,防止下次升级被顺手升上去:
sudo apt-get install nginx=1.24.0-2ubuntu7 # 示例版本号,以 madison 的实际输出为准
sudo apt-mark hold nginx # 锁定,upgrade 时跳过它
apt-mark showhold # 查看哪些包被锁定
sudo apt-mark unhold nginx # 解除锁定
厂商单独提供的 .deb(监控 agent、阵列卡管理工具之类)有两种装法。用 dpkg 装,它不管依赖,缺什么就报错,留下一个“装了一半”的包:
sudo dpkg -i ./agent_2.1.0_amd64.deb
# 缺依赖时报 dependency problems prevent configuration of agent
sudo apt-get install -f # 补装缺失的依赖,并完成这个包的配置
更省事的做法是直接让 apt-get 装本地文件,依赖一并解决。注意路径要带 ./ 或写成绝对路径,否则 apt-get 会把它当成软件源里的包名去找:
sudo apt-get install ./agent_2.1.0_amd64.deb
apt-get install -f 不是万能的。它的目标是让依赖关系恢复一致,如果某个包需要的依赖在当前系统上根本装不上(比如 .deb 是给别的发行版版本打的),它给出的“修复”方案就是把这个包,甚至别的包一起删掉。按 Y 之前,一定看清 The following packages will be REMOVED 下面列了什么。
卸载与清理:remove、purge、autoremove 的区别
以 nginx 为例走一遍:
sudo apt-get remove nginx # 删程序,/etc/nginx 还在
dpkg -l | grep '^rc' # 列出“已卸载但配置还在”的包
sudo apt-get purge nginx nginx-common # 连配置一起删
sudo apt-get autoremove --purge # 顺带删掉当初自动装上、现在没人需要的依赖
dpkg -l 第一列的 rc 表示期望状态是删除(r),当前只剩配置文件(c)。一次性清掉所有残留配置:
dpkg -l | awk '/^rc/ {print $2}' | xargs -r sudo apt-get purge -y
三点要注意:
- purge 只删包管理器登记过的文件,网站目录、上传文件这类你自己放进去的内容不会删。部分数据库软件包在 purge 时会询问是否删除数据目录,看清再回答。
- autoremove 依据的是“自动安装”标记。依赖包装上时被标记为 auto,不再被任何包需要时就会被 autoremove 删掉。某个依赖你想单独留着,执行
apt-mark manual 包名改成手动安装;apt-mark showauto能看到全部自动安装的包。卸载一个元包(比如桌面环境的总包)之后,autoremove 可能列出一大串要删的软件,先加-s模拟再决定。 - 旧内核也靠 autoremove 清理。APT 会自动保护正在运行的内核和最新的内核不被删除,更早的旧内核会出现在 autoremove 的列表里。
/boot分区不大的机器,内核升级几次后可能装不下新内核,定期执行一次即可,不要手工删/boot里的文件。
下载缓存默认留在 /var/cache/apt/archives,用 du -sh /var/cache/apt/archives 看占了多少,apt-get clean 全部清掉,以后需要时会重新下载。
apt-get download 与离线安装
apt-get download 包名 把 .deb 下载到当前目录,不需要 root,也不会安装。单独拿一个包时很方便,比如想看看包里有哪些文件:
apt-get download ipmitool
dpkg -c ipmitool_*.deb # 列出包内文件
dpkg -x ipmitool_*.deb ./x # 解包到 ./x 目录,不安装
它不带依赖,给没有外网的服务器准备安装包时,要把依赖一起下载。在一台能上网、发行版版本和 CPU 架构都与目标机相同的机器上执行:
mkdir ipmitool-debs && cd ipmitool-debs
apt-get download $(apt-cache depends --recurse --no-recommends --no-suggests \
--no-conflicts --no-breaks --no-replaces --no-enhances ipmitool | grep '^\w' | sort -u)
这样会把整条依赖链都下载下来,其中不少目标机上已经装了,安装时同版本的包会被跳过。如果报 Can't select candidate version from package ... as it has no candidate,说明列表里混进了虚拟包名(如 debconf-2.0),它没有对应的 .deb,把这个名字从列表里去掉再下载即可。把目录拷到目标机后安装:
sudo apt-get install ./*.deb
版本必须对应:给 Ubuntu 24.04 准备的包,就要在 24.04 上下载,混用版本是离线安装失败最常见的原因。机房里要维护的机器一多,与其每次手工搬包,不如在内网放一台 apt-cacher-ng 做缓存代理(默认端口 3142),各服务器在 /etc/apt/apt.conf.d/ 下新建一个文件,写入 Acquire::http::Proxy "http://10.10.0.5:3142";,同一个包只从外网下载一次。软件源地址是 https 的,缓存需要另外配置,以 apt-cacher-ng 文档为准。
服务器上常见的 apt-get 报错
| 报错 | 原因 | 处理 |
|---|---|---|
E: Could not get lock /var/lib/dpkg/lock-frontend. It is held by process 1234 (unattended-upgr) |
另一个 apt 或 dpkg 进程正在运行;Ubuntu 刚开机时常是自动安全更新 | 等它结束,用 ps -fp 1234 看是什么进程。不要删锁文件,apt 自己也会提示 removing the lock file is not a solution |
E: dpkg was interrupted, you must manually run 'sudo dpkg --configure -a' to correct the problem. |
上次安装或升级中途断了,比如 SSH 断线、进程被杀 | 照提示执行 sudo dpkg --configure -a,再执行 sudo apt-get install -f |
E: Unable to locate package ipmitools |
没有 update、包名写错(这里多打了一个 s),或者包所在的组件(如 Ubuntu 的 universe)没有启用 | apt-get update 后用 apt-cache search 关键词 找正确的包名;启用 universe:sudo add-apt-repository universe |
The following packages have unmet dependencies 和 you have held broken packages |
第三方源的包与当前系统版本不匹配,或有包被 hold | apt-cache policy 包名 看候选版本来自哪个源;apt-mark showhold 看锁定的包 |
You might want to run 'apt --fix-broken install' to correct these. |
系统里已经有依赖不完整的包,之后的安装都会被拦住 | 执行 sudo apt-get install -f,看清要删除的包再确认 |
在远程服务器上做大批量升级,放在 tmux 或 screen 里执行,SSH 断了升级也不会被打断,这是避免出现第二行报错最简单的办法。写进脚本时,用下面的写法避免升级卡在交互提问上:
export DEBIAN_FRONTEND=noninteractive
apt-get update -q
apt-get -y -o Dpkg::Options::="--force-confdef" -o Dpkg::Options::="--force-confold" dist-upgrade
DEBIAN_FRONTEND=noninteractive 让安装脚本不弹出配置对话框;--force-confdef 和 --force-confold 合起来的意思是:新包带来的配置文件与你改过的版本冲突时,有默认处理方式就按默认处理,否则保留你现有的配置,不会被新版本覆盖。
升级内核后起不来:在机房里怎么兜底
dist-upgrade 装上新内核,重启后卡住或网卡不认,是批量升级时最怕遇到的情况。这时 SSH 已经连不上,只能从带外进去:在 Toplink DCIM 的 IPMI 远程管理里打开走 BMC 通道的 VNC 控制台,重启时调出 GRUB 菜单。Ubuntu Server 默认隐藏这个菜单,UEFI 启动的机器在启动时按 Esc,传统 BIOS 启动的按住 Shift,进入 Advanced options for Ubuntu 选上一个内核启动。进系统后先把问题内核卸掉,或用 apt-mark hold 暂缓升级,查清原因再处理。
如果系统连旧内核也进不去,租用服务器的客户可以在客户自助端切到救援模式,挂载原来的系统盘,用 chroot 进入后执行 dpkg --configure -a 或重装内核包,硬盘上的数据不受影响。批量升级前先挑一两台做验证,确认重启正常再全面铺开,比事后救火省事得多。
常见问题
apt-get 一定要用 root 执行吗?
会改动系统的子命令(update、install、remove、upgrade、clean 等)需要 root 或 sudo。apt-get download、加 -s 的模拟执行,以及 apt-cache 的各种查询,普通用户就能运行。
怎么查一个命令属于哪个软件包?
已经装上的,用 dpkg -S 加命令的完整路径,例如 dpkg -S /usr/bin/ipmitool;还没装的,先 apt-get install apt-file,执行 apt-file update 后用 apt-file search bin/命令名 查找。Ubuntu 上直接输入一个没装的命令,通常也会提示该装哪个包。
/etc/apt/sources.list 里只剩注释,软件源配在哪里?
Ubuntu 24.04 起默认改用 deb822 格式,软件源写在 /etc/apt/sources.list.d/ubuntu.sources 里,一个源是一段 Types、URIs、Suites、Components 字段。换镜像就改这个文件的 URIs 行,改完执行 apt-get update。