dpkg 命令详解与参数实测(Ubuntu 22.04)

dpkg 命令详解:在 Ubuntu 22.04(dpkg 1.21.1)环境逐参数实测 -i/-r/-P 安装删除、-l 状态码 ii/rc/un 逐列解读、-L/-S/-s 查询、--unpack/--configure 分步修复、--get-selections/--set-selections、-c/--info 包内审阅、-C 审计与 --verify 校验,并以实测证明 dpkg 无依赖解析及 dpkg 与 apt 的分工。

Linux命令Linux系统UbuntuUbuntu 22.04命令行包管理2026/10/16

dpkg 命令详解与参数实测(Ubuntu 22.04)

目标:全面掌握 dpkg 的每一个常用参数——-i/-r/-P 安装删除、-l/-L/-S/-s 状态查询、--unpack/--configure 分步控制、--get-selections/--set-selections 选中状态、-c/--info/-f 包内审阅、-C 审计与 --verify 校验,全部命令与回显已在 Ubuntu 22.04(dpkg 1.21.1)实测通过。

一、环境信息

本篇所有命令均在以下环境中逐条真实执行,回显为实际输出(非编造):

  • 操作系统:Ubuntu 22.04.5 LTS(x86_64),内核 6.8.0-138-generic
  • dpkg 版本:程序自报 1.21.1 (amd64),软件包版本 1.21.1ubuntu2.6
  • apt 版本:apt 2.4.14 (amd64)(配合 install -f 修依赖时使用)
  • 状态数据库:/var/lib/dpkg/status(包状态)、/var/lib/dpkg/available(可选库)
  • 测试数据包:sl 5.02-1(13K,无 conffiles)、cowsay 3.03+dfsg2-8(依赖 libtext-charwidth-perl)、plocate 1.1.15-1ubuntu2(含 conffile /etc/updatedb.conf)
  • 全部 .deb 通过 apt-get download 从官方源获取,路径 /root/*.deb

二、命令概览

2.1 语法与测试数据

dpkg 是 Debian 系的底层包管理器,直接操作 .deb 文件与状态数据库,不联网、不解析依赖:

dpkg [<选项>...] <命令>
dpkg -i <.deb 文件>          # 安装本地包
dpkg -r <包名>               # 删除(保留配置文件)
dpkg -P <包名>               # 清除(连配置文件一起删)
dpkg -l [模式]               # 列出包及状态码
dpkg -L <包名>               # 列出包安装的所有文件
dpkg -S <文件路径|模式>      # 反查文件属于哪个包
dpkg -s <包名>               # 显示包状态详情

常用命令一览:

命令作用本文实测
-i / --install安装本地 .deb是(3.2、3.3 节)
--unpack / --configure解包与配置分步执行是(3.4 节)
-r / --remove删除包,保留 conffiles是(3.5 节)
-P / --purge清除包及 conffiles是(3.6 节)
-l / --list列表与状态码是(3.1 节)
-L / --listfiles包文件清单是(3.7 节)
-S / --search文件反查包是(3.8 节)
-s / --status包状态详情是(3.9 节)
-c / --info(经 dpkg-deb)审阅未安装的 .deb是(3.10 节)
--get-selections / --set-selections读写选中状态是(3.11 节)
-C / --audit审计损坏/半成品包是(3.12 节)
-V / --verify校验已装文件完整性是(3.12 节)

💡 技术拓展:dpkg 与 apt 是「引擎与导航」的分工关系——

工具定位依赖解析典型用途
dpkg底层引擎:直接解包 .deb、维护状态库无——依赖缺失时报错并把包留在未配置状态安装本地 .deb、查文件归属、状态审计
apt / apt-get前端:面向软件源,读 dpkg 状态库做决策有——自动算出依赖闭包并下载补齐在线安装/升级/批量依赖处理

本文 3.3 节用实测证明了「dpkg 无依赖解析」:dpkg -i 缺依赖直接 rc=1 留下 iU 状态,随后 apt-get install -f 才补齐依赖并完成配置。两者是互补而非替代。

测试数据包说明:sl(火车动画小游戏,装在 /usr/games/sl)体积小、无 conffiles,适合演示安装/删除;cowsay(会说话的牛)依赖 libtext-charwidth-perl,用于构造缺依赖场景;plocate(文件定位工具)自带 conffile /etc/updatedb.conf,用于演示 ii→rc→清除 的完整状态迁移。

2.2 --help 全量输出(逐行照抄)

$ dpkg --help 2>&1
Usage: dpkg [<option>...] <command>

Commands:
  -i|--install       <.deb file name>... | -R|--recursive <directory>...
  --unpack           <.deb file name>... | -R|--recursive <directory>...
  -A|--record-avail  <.deb file name>... | -R|--recursive <directory>...
  --configure        <package>... | -a|--pending
  --triggers-only    <package>... | -a|--pending
  -r|--remove        <package>... | -a|--pending
  -P|--purge         <package>... | -a|--pending
  -V|--verify [<package>...]       Verify the integrity of package(s).
  --get-selections [<pattern>...]  Get list of selections to stdout.
  --set-selections                 Set package selections from stdin.
  --clear-selections               Deselect every non-essential package.
  --update-avail [<Packages-file>] Replace available packages info.
  --merge-avail [<Packages-file>]  Merge with info from file.
  --clear-avail                    Erase existing available info.
  --forget-old-unavail             Forget uninstalled unavailable pkgs.
  -s|--status [<package>...]       Display package status details.
  -p|--print-avail [<package>...]  Display available version details.
  -L|--listfiles <package>...      List files 'owned' by package(s).
  -l|--list [<pattern>...]         List packages concisely.
  -S|--search <pattern>...         Find package(s) owning file(s).
  -C|--audit [<package>...]        Check for broken package(s).
  --yet-to-unpack                  Print packages selected for installation.
  --predep-package                 Print pre-dependencies to unpack.
  --add-architecture <arch>        Add <arch> to the list of architectures.
  --remove-architecture <arch>     Remove <arch> from the list of architectures.
  --print-architecture             Print dpkg architecture.
  --print-foreign-architectures    Print allowed foreign architectures.
  --assert-help                    Show help on assertions.
  --assert-<feature>               Assert support for the specified feature.
  --validate-<thing> <string>      Validate a <thing>'s <string>.
  --compare-versions <a> <op> <b>  Compare version numbers - see below.
  --force-help                     Show help on forcing.
  -Dh|--debug=help                 Show help on debugging.

  -?, --help                       Show this help message.
      --version                    Show the version.

Validatable things: pkgname, archname, trigname, version.

Use dpkg with -b, --build, -c, --contents, -e, --control, -I, --info,
  -f, --field, -x, --extract, -X, --vextract, --ctrl-tarfile, --fsys-tarfile
on archives (type dpkg-deb --help).

Options:
  --admindir=<directory>     Use <directory> instead of /var/lib/dpkg.
  --root=<directory>         Install on a different root directory.
  --instdir=<directory>      Change installation dir without changing admin dir.
  --pre-invoke=<command>     Set a pre-invoke hook.
  --post-invoke=<command>    Set a post-invoke hook.
  --path-exclude=<pattern>   Do not install paths which match a shell pattern.
  --path-include=<pattern>   Re-include a pattern after a previous exclusion.
  -O|--selected-only         Skip packages not selected for install/upgrade.
  -E|--skip-same-version     Skip packages whose same version is installed.
  -G|--refuse-downgrade      Skip packages with earlier version than installed.
  -B|--auto-deconfigure      Install even if it would break some other package.
  --[no-]triggers            Skip or force consequential trigger processing.
  --verify-format=<format>   Verify output format (supported: 'rpm').
  --no-pager                 Disables the use of any pager.
  --no-debsig                Do not try to verify package signatures.
  --no-act|--dry-run|--simulate
                             Just say what we would do - don't do it.
  -D|--debug=<octal>         Enable debugging (see -Dhelp or --debug=help).
  --status-fd <n>            Send status change updates to file descriptor <n>.
  --status-logger=<command>  Send status change updates to <command>'s stdin.
  --log=<filename>           Log status changes and actions to <filename>.
  --ignore-depends=<package>[,...]
                             Ignore dependencies involving <package>.
  --force-<thing>[,...]      Override problems (see --force-help).
  --no-force-<thing>[,...]   Stop when problems encountered.
  --refuse-<thing>[,...]     Ditto.
  --abort-after <n>          Abort after encountering <n> errors.
  --robot                    Use machine-readable output on some commands.

Comparison operators for --compare-versions are:
  lt le eq ne ge gt       (treat empty version as earlier than any version);
  lt-nl le-nl ge-nl gt-nl (treat empty version as later than any version);
  < << <= = >= >> >       (only for compatibility with control file syntax).

Use 'apt' or 'aptitude' for user-friendly package management.

注意帮助末尾那句官方提示:Use 'apt' or 'aptitude' for user-friendly package management.——dpkg 定位就是底层工具,日常在线安装交给 apt。对 .deb 文件本身的操作(-c/--info/-f 等)由同族的 dpkg-deb 提供。

2.3 版本信息

$ dpkg --version
Debian 'dpkg' package management program version 1.21.1 (amd64).
This is free software; see the GNU General Public License version 2 or
later for copying conditions. There is NO warranty.

$ dpkg -l dpkg | tail -1
ii  dpkg           1.21.1ubuntu2.6 amd64        Debian package management system

程序自报版本 1.21.1(上游版本号),软件包版本为 1.21.1ubuntu2.6(含 Ubuntu 补丁的后缀)。查包版本用 dpkg -l dpkg,查程序版本用 dpkg --version,两者口径不同、各有用途。

三、参数逐项实测

3.1 -l:列表与状态码(重点:ii/rc/un 逐列解读)

dpkg -l 是状态巡检的入口。输出开头的 3 行图例就是状态码的官方解释:

$ dpkg -l | head -8
Desired=Unknown/Install/Remove/Purge/Hold
| Status=Not/Inst/Conf-files/Unpacked/halF-conf/Half-inst/trig-aWait/Trig-pend
|/ Err?=(none)/Reinst-required (Status,Err: uppercase=bad)
||/ Name                            Version                                 Architecture Description
+++-===============================-=======================================-============-================================================================================
ii  adduser                         3.118ubuntu5                            all          add and remove users and groups
ii  apparmor                        3.0.4-2ubuntu2.5                        amd64        user-space parser utility for AppArmor
ii  apt                             2.4.14                                  amd64        commandline package manager

每个包行前三列字符逐一解读(后跟错误位,大写=异常):

列含义取值
第 1 列 Desired期望动作i 安装、r 删除、p 清除、h 锁定、u 未知
第 2 列 Status当前状态i 已安装、c 仅剩配置文件、n 未安装、u 已解包未配置、f 半配置、h 半安装、w/t 触发器等待/挂起
第 3 列 Err错误标志空格正常、R 需要重装

本文实测出现过的组合及含义:ii(期望安装、已安装,正常态)、rc(期望删除、只剩配置文件)、iU(期望安装、解包未配置)、in(期望安装、尚未安装)、un(无选中、未安装)。查询单个包同样带图例头:

$ dpkg -l libc6
Desired=Unknown/Install/Remove/Purge/Hold
| Status=Not/Inst/Conf-files/Unpacked/halF-conf/Half-inst/trig-aWait/Trig-pend
|/ Err?=(none)/Reinst-required (Status,Err: uppercase=bad)
||/ Name           Version          Architecture Description
+++-==============-================-============-=================================
ii  libc6:amd64    2.35-0ubuntu3.15 amd64        GNU C Library: Shared libraries

$ dpkg -l not-exist-pkg 2>&1; echo rc=$?
dpkg-query: no packages found matching not-exist-pkg
rc=1

💡 要点:dpkg -l 包名 查不到时退出码为 1 而非 100——dpkg 查询类错误(dpkg-query)与操作类错误(rc=100)返回码不同,写脚本时注意区分。

3.2 -i:安装本地 .deb(依赖满足直接成功)

先用 apt-get download 取一个真实 .deb,再离线安装:

$ cd /root && apt-get download sl 2>&1 | tail -1
W: Download is performed unsandboxed as root as file '/root/sl_5.02-1_amd64.deb' couldn't be accessed by user '_apt'. - pkgAcquire::Run (13: Permission denied)

$ ls -lh /root/sl_*.deb
-rw-r--r-- 1 root root 13K Feb  2  2019 /root/sl_5.02-1_amd64.deb

$ dpkg -i /root/sl_*.deb; echo rc=$?
Selecting previously unselected package sl.
(Reading database ... 26317 files and directories currently installed.)
Preparing to unpack /root/sl_5.02-1_amd64.deb ...
Unpacking sl (5.02-1) ...
Setting up sl (5.02-1) ...
rc=0

$ dpkg -l sl | tail -1
ii  sl             5.02-1       amd64        Correct you if you type `sl' by mistake

流程四步:Selecting(选中)→ Unpacking(解包)→ Setting up(配置)→ rc=0。下载时的 W: Download is performed unsandboxed as root 是以 root 直接下载的提示(apt 希望用 _apt 用户沙箱下载),不影响结果,属常见现象。

sl 的依赖(libc6/libncurses6/libtinfo6)系统本就满足,所以 -i 一步到位。若运行它,由于 sl 是全屏动画程序、需要终端环境,无终端时如实报错:

💡 要点:dpkg -i 是纯本地安装器——只认命令行给到的 .deb 文件,不会去仓库查找缺失依赖(3.3 节实证)。它适合「已备齐全部 .deb 的离线场景」;联网机器装第三方 .deb 更省心的写法是 apt install ./xxx.deb,让 apt 顺手补依赖。开头的 W: Download is performed unsandboxed as root 是以 root 身份执行 apt-get download 的沙箱提示,不影响安装结果。

$ /usr/games/sl 2>&1 | head -4
Error opening terminal: unknown.

3.3 -i 缺依赖:dependency problems(rc=1),证明 dpkg 无依赖解析

cowsay 依赖 libtext-charwidth-perl,先卸掉它再 dpkg -i,观察真实失败形态:

$ dpkg -P sl >/dev/null 2>&1; echo sl-purged
sl-purged

$ cd /root && apt-get download cowsay 2>&1 | tail -1
W: Download is performed unsandboxed as root as file '/root/cowsay_3.03+dfsg2-8_all.deb' couldn't be accessed by user '_apt'. - pkgAcquire::Run (13: Permission denied)

$ dpkg -i /root/cowsay_*.deb; echo rc=$?
Selecting previously unselected package cowsay.
(Reading database ... 26317 files and directories currently installed.)
Preparing to unpack .../cowsay_3.03+dfsg2-8_all.deb ...
Unpacking cowsay (3.03+dfsg2-8) ...
dpkg: dependency problems prevent configuration of cowsay:
 cowsay depends on libtext-charwidth-perl; however:
  Package libtext-charwidth-perl is not installed.

dpkg: error processing package cowsay (--install):
 dependency problems - leaving unconfigured
Errors were encountered while processing:
 cowsay
rc=1

$ dpkg -l cowsay | tail -1
iU  cowsay         3.03+dfsg2-8 all          configurable talking cow

$ dpkg -C; echo rc=$?
The following packages have been unpacked but not yet configured.
They must be configured using dpkg --configure or the configure
menu option in dselect for them to work:
 cowsay               configurable talking cow

rc=0

失败形态三要素:报错段 dependency problems prevent configuration、退出码 rc=1、状态停在 iU(解包未配置)。dpkg -C 审计也能立刻发现这个半成品。此时 dpkg 不会自己去装 libtext-charwidth-perl——补依赖交给 apt 的 -f(fix-broken):

$ DEBIAN_FRONTEND=noninteractive apt-get install -f -y -o Dpkg::Use-Pty=0 2>&1 | tail -6
Selecting previously unselected package libtext-charwidth-perl.
(Reading database ... 26377 files and directories currently installed.)
Preparing to unpack .../libtext-charwidth-perl_0.04-10build3_amd64.deb ...
Unpacking libtext-charwidth-perl (0.04-10build3) ...
Setting up libtext-charwidth-perl (0.04-10build3) ...
Setting up cowsay (3.03+dfsg2-8) ...

$ dpkg -l cowsay | tail -1
ii  cowsay         3.03+dfsg2-8 all          configurable talking cow

$ /usr/games/cowsay dpkg works
 ____________
< dpkg works >
 ------------
        \   ^__^
         \  (oo)\_______
            (__)\       )\/\
                ||----w |
                ||     ||

apt-get install -f 补装缺失依赖并顺势把 cowsay 配置完成(iU → ii),程序可正常运行。这就是「dpkg 装包 + apt 修依赖」的标准组合拳。

💡 要点:这套二段式是 Ubuntu 离线/半离线装包的固定套路:dpkg 负责把 .deb 落盘解包,apt 负责从仓库补齐依赖并完成配置。脚本化时写成 dpkg -i xxx.deb || apt-get install -f -y,并把 dpkg -C(审计半成品)接在后面做验证,能覆盖绝大多数离线安装失败形态。

3.4 --unpack 与 --configure:分步安装,--configure -a 修复半配置

把安装拆成两步,观察状态变化:

$ dpkg --unpack /root/sl_*.deb; echo rc=$?
Selecting previously unselected package sl.
(Reading database ... 26317 files and directories currently installed.)
Preparing to unpack /root/sl_5.02-1_amd64.deb ...
Unpacking sl (5.02-1) ...
rc=0

$ dpkg -l sl | tail -1
iU  sl             5.02-1       amd64        Correct you if you type `sl' by mistake

$ dpkg --configure sl; echo rc=$?
Setting up sl (5.02-1) ...
rc=0

$ dpkg -l sl | tail -1
ii  sl             5.02-1       amd64        Correct you if you type `sl' by mistake

--unpack 只解文件不配置(iU),--configure 包名 补上配置(ii)。--configure -a 则扫描所有未配置/半配置的包逐一修复,健康系统上无事可做、静默返回 rc=0:

$ dpkg --configure -a; echo rc=$?
rc=0

💡 要点:apt 升级中途断电/被杀后,最常见的修复口诀就是「先 dpkg --configure -a,再 apt-get install -f」——前者把解包未配置的收尾,后者补齐缺失依赖。

3.5 -r:删除包(ii → rc)

用带 conffile 的 plocate 演示。删除前确认 /etc/updatedb.conf 存在:

$ DEBIAN_FRONTEND=noninteractive apt-get install -y -qq -o Dpkg::Use-Pty=0 plocate >/dev/null 2>&1; echo install_rc=$?
install_rc=0

$ dpkg -l plocate
Desired=Unknown/Install/Remove/Purge/Hold
| Status=Not/Inst/Conf-files/Unpacked/halF-conf/Half-inst/trig-aWait/Trig-pend
|/ Err?=(none)/Reinst-required (Status,Err: uppercase=bad)
||/ Name           Version         Architecture Description
+++-==============-===============-============-=================================
ii  plocate        1.1.15-1ubuntu2 amd64        much faster locate

$ ls -l /etc/updatedb.conf
-rw-r--r-- 1 root root 568 Feb  9  2022 /etc/updatedb.conf

$ dpkg -r plocate; echo rc=$?
(Reading database ... 26333 files and directories currently installed.)
Removing plocate (1.1.15-1ubuntu2) ...
rc=0

$ dpkg -l plocate
Desired=Unknown/Install/Remove/Purge/Hold
| Status=Not/Inst/Conf-files/Unpacked/halF-conf/Half-inst/trig-aWait/Trig-pend
|/ Err?=(none)/Reinst-required (Status,Err: uppercase=bad)
||/ Name           Version         Architecture Description
+++-==============-===============-============-=================================
rc  plocate        1.1.15-1ubuntu2 amd64        much faster locate

$ ls -l /etc/updatedb.conf
-rw-r--r-- 1 root root 568 Feb  9  2022 /etc/updatedb.conf

验证闭环:状态从 ii 变 rc,而 conffile /etc/updatedb.conf 原样保留(568 字节未动)——-r 的语义就是「删程序、留配置」,方便下次重装沿用手工调过的配置。

💡 要点:rc 状态包是「已卸载但配置残留」的标记,巡检时用 dpkg -l | awk '/^rc/{print $2}' 列出,确认无用后可 xargs dpkg -P 批量清除。选 -r 还是 -P 的判断标准只有一个:这些 conffile 里有没有手工改动且将来还要用——有则 -r,没有则 -P。

3.6 -P:清除包(rc → 记录消失)

$ dpkg -P plocate; echo rc=$?
(Reading database ... 26320 files and directories currently installed.)
Purging configuration files for plocate (1.1.15-1ubuntu2) ...
rc=0

$ dpkg -l plocate 2>&1; echo rc=$?
dpkg-query: no packages found matching plocate
rc=1

-P 输出 Purging configuration files for plocate,随后记录从状态库中彻底消失(查询报 no packages found、rc=1)。对比 -r:无 conffiles 的包(如本文的 sl)在 -r 后记录也会直接消失,因为没有任何东西可留。

💡 要点:-P 不可逆——包记录从 /var/lib/dpkg/status 彻底移除,之后 dpkg -s/-L 都查不到任何痕迹。有「卸载审计」要求的服务器建议日常只用 -r(保留 rc 记录作为卸载凭证),定期统一清理 rc 状态包。

3.7 -L:列出包安装的所有文件

$ dpkg -L sl | head -8
/.
/usr
/usr/games
/usr/games/sl
/usr/share
/usr/share/doc
/usr/share/doc/sl
/usr/share/doc/sl/README

💡 要点:dpkg -L 是「包 → 文件」方向,可执行文件在哪个目录(如 /usr/games)一眼可见;配合 dpkg -S 的「文件 → 包」方向形成互查闭环。

3.8 -S:文件反查属于哪个包

$ dpkg -S /usr/games/sl
sl: /usr/games/sl

$ dpkg -S /bin/ls
coreutils: /bin/ls

$ dpkg -S /etc/hostname 2>&1; echo rc=$?
dpkg-query: no path found matching pattern /etc/hostname
rc=1

/bin/ls 属于 coreutils;而 /etc/hostname 查不到——它由系统安装流程生成,不属于任何软件包,属正常现象。-S 在排查「这个二进制/配置是谁家的」时非常好用。

💡 要点:dpkg -S 的检索范围仅限已安装的包;要回答「仓库里哪个包能提供这个文件」(还没装的情况),需安装 apt-file 后用 apt-file search 路径。排查陌生二进制来源时先用 -S(零安装成本),查不到再上 apt-file。

3.9 -s:包状态详情

$ dpkg -s sl | head -12
Package: sl
Status: install ok installed
Priority: optional
Section: games
Installed-Size: 59
Maintainer: Ubuntu Developers <ubuntu-devel-discuss@lists.ubuntu.com>
Architecture: amd64
Version: 5.02-1
Depends: libc6 (>= 2.2.5), libncurses6 (>= 6), libtinfo6 (>= 6)
Description: Correct you if you type `sl' by mistake
 Sl is a program that can display animations aimed to correct you
 if you type 'sl' by mistake.

第二行 Status: install ok installed 就是 -l 里 ii 的全拼(期望动作 ok 状态)。比 -l 更详细的还有 Version、Depends、Installed-Size 等字段,脚本解析建议用 dpkg-query -W -f 自定义格式。

💡 要点:dpkg -s 的输出面向人读,字段顺序与内容随包而变;脚本取单一字段务必改用 dpkg-query -W -f='${Status} ${Version}\n' 包名,一行拿到结构化结果(如判断 install ok installed),避免解析长文本的脆弱性。

3.10 -c / --info / dpkg-deb -f:审阅未安装的 .deb

不安装也能看包内容与元信息(对 .deb 的操作由 dpkg-deb 引擎承接):

$ dpkg -c /root/sl_*.deb | head -8
drwxr-xr-x root/root         0 2019-02-02 11:57 ./
drwxr-xr-x root/root         0 2019-02-02 11:57 ./usr/
drwxr-xr-x root/root         0 2019-02-02 11:57 ./usr/games/
-rwxr-xr-x root/root     26568 2019-02-02 11:57 ./usr/games/sl
drwxr-xr-x root/root         0 2019-02-02 11:57 ./usr/share/
drwxr-xr-x root/root         0 2019-02-02 11:57 ./usr/share/doc/
drwxr-xr-x root/root         0 2019-02-02 11:57 ./usr/share/doc/sl/
-rw-r--r-- root/root       360 2019-01-29 12:15 ./usr/share/doc/sl/README
dpkg-deb: error: tar subprocess was killed by signal (Broken pipe)

$ dpkg --info /root/sl_*.deb
 new Debian package, version 2.0.
 size 12708 bytes: control archive=960 bytes.
     516 bytes,    14 lines      control              
    1021 bytes,    16 lines      md5sums              
 Package: sl
 Version: 5.02-1
 Architecture: amd64
 Maintainer: Ubuntu Developers <ubuntu-devel-discuss@lists.ubuntu.com>
 Original-Maintainer: Markus Frosch <lazyfrosch@debian.org>
 Installed-Size: 59
 Depends: libc6 (>= 2.2.5), libncurses6 (>= 6), libtinfo6 (>= 6)
 Section: games
 Priority: optional
 Homepage: https://github.com/mtoyoda/sl
 Description: Correct you if you type `sl' by mistake
  Sl is a program that can display animations aimed to correct you
  if you type 'sl' by mistake.
  SL stands for Steam Locomotive.

$ dpkg-deb -f /root/sl_*.deb Package Version Architecture Depends
Package: sl
Version: 5.02-1
Architecture: amd64
Depends: libc6 (>= 2.2.5), libncurses6 (>= 6), libtinfo6 (>= 6)

-c 列文件清单(权限/属主/大小),--info 展示 control 元信息与校验摘要,dpkg-deb -f 按字段名精确提取——脚本里提取单个字段(如 Depends)优先用第三种。注意 -c | head 结尾的 Broken pipe 报错是管道截断(head 只取 8 行)触发的 SIGPIPE,属正常现象,不影响结果;不想要这条报错可改用 dpkg-deb -c 包.deb > 文件 后再截取。

💡 要点:审计第三方 .deb 的标准三连——--info 看元信息与依赖声明 → -c 看落盘路径清单(重点核对有没有往 /etc/systemd/system、/etc/cron.* 塞自启项)→ 确认无异常再 -i。dpkg-deb -f 字段名 是脚本提取单字段的唯一可靠方式(依赖名、版本号等),别用 -c/--info 的输出做文本解析。

3.11 --get-selections / --set-selections:读写选中状态

选中状态是比「是否安装」更底层的一层:即使包未装,也可以先「登记」期望动作(install/hold/deinstall/purge)。先看全量:

$ dpkg --get-selections | head -5
adduser						install
apparmor					install
apt						install
base-files					install
base-passwd					install

$ dpkg --get-selections | wc -l
353

$ dpkg --get-selections bash
bash						install

构造一条「未安装但已登记」的记录:--record-avail 把 .deb 元信息写入 available 库,随后 --set-selections 才能识别它:

$ dpkg --record-avail /root/sl_*.deb; echo rc=$?
Recorded info about sl from /root/sl_5.02-1_amd64.deb.
rc=0

$ echo 'sl install' | dpkg --set-selections; echo rc=$?
rc=0

$ dpkg --get-selections sl; echo rc=$?
sl						install
rc=0

$ dpkg -l sl | tail -1
in  sl             <none>       amd64        (no description available)

$ echo 'sl deinstall' | dpkg --set-selections; echo rc=$?
rc=0

$ dpkg -l sl | tail -1
un  sl             <none>       amd64        (no description available)

验证闭环:登记 install 后状态行显示 in(期望安装、未安装),改登记 deinstall 后显示 un(无选中、未安装)。apt-mark 的 hold 本质就是把选中状态写成 hold——echo '包名 hold' | dpkg --set-selections 与 apt-mark hold 包名 等效,可用 dpkg --get-selections 包名 交叉验证(实测见 hold 相关章节)。

💡 要点:这组命令是 apt-mark hold/unhold 的底层实现——批量锁版的脚本写法是「echo 多行『包名 hold』 | dpkg --set-selections」一次灌入,比循环调 apt-mark 快得多。in/un 这类「登记未装」状态是 available 库残留的中间态,正常系统不应长期存在,巡检时见到可用 dpkg --clear-avail 清理。

3.12 -C:审计半成品,--verify:校验文件完整性

健康系统上两个命令都应静默通过:

$ dpkg -C; echo rc=$?
rc=0

$ dpkg --verify; echo rc=$?
rc=0

-C 输出为空表示没有未配置/半安装/损坏的包;--verify 对照每个包的 md5sums 清单校验已装文件,输出为空表示所有文件与安装时一致。3.3 节曾实测过 -C 报告 iU 半成品包的形态,两者对照即是完整的「审计 → 修复 → 复查」闭环。

💡 要点:两个命令的分工——-C 管「装好了没」(dpkg 状态机视角),--verify 管「文件被动过没」(md5sums 基线视角)。批量安装脚本收尾跑 -C 做断言,安全巡检定时跑 --verify 抓配置篡改;两者都是「静默即健康」,写脚本时按非空输出报警处理即可。

3.13 技术拓展:dpkg 与 apt 家族的分工对比

Debian 系包管理命令不止 dpkg 一个,初学者常混淆「谁负责什么」。主流工具横向对比——

工具层级依赖解析典型用途
dpkg底层包引擎无(3.3 节实证)装本地 .deb、查状态/文件清单、选中状态管理
apt-get高层前端有(自动补依赖)脚本化安装/升级,输出稳定、rc 约定清晰
apt高层前端有交互式日常使用,进度条 + 精简输出(面向人)
apt-cache高层查询不涉及(只读)离线检索包元信息(不碰网络,走本地缓存)
dpkg-deb底层工具不涉及审阅/解包/构建单个 .deb 文件
aptitude另一套前端有交互式 TUI 与更智能的冲突解决(Ubuntu 默认未装)

💡 关键差异一句话:dpkg 管「单个包的落地与状态」,apt 系管「依赖网络的求解」——两者是引擎与调度层的关系,不是竞争关系。本文以 dpkg 为主线实测其底层能力(状态码、选中状态、文件反查),依赖求解场景一律交回 apt 系处理(如 3.3 节的 apt-get install -f)。

四、实战示例

4.1 离线安装 .deb 及其依赖(一次 dpkg -i 多包)

场景:内网服务器拿不到软件源,先在有网机器 apt-get download 把主包和依赖全部下齐,拷贝过去一次装完:

$ cd /root && apt-get download cowsay libtext-charwidth-perl 2>&1 | tail -1
W: Download is performed unsandboxed as root as file '/root/libtext-charwidth-perl_0.04-10build3_amd64.deb' couldn't be accessed by user '_apt'. - pkgAcquire::Run (13: Permission denied)

$ ls -lh /root/cowsay_*.deb /root/libtext-charwidth-perl_*.deb
-rw-r--r-- 1 root root 19K May 12  2020 /root/cowsay_3.03+dfsg2-8_all.deb
-rw-r--r-- 1 root root 10K Feb  6  2022 /root/libtext-charwidth-perl_0.04-10build3_amd64.deb

$ dpkg -i /root/cowsay_*.deb /root/libtext-charwidth-perl_*.deb; echo rc=$?
Selecting previously unselected package cowsay.
(Reading database ... 26317 files and directories currently installed.)
Preparing to unpack .../cowsay_3.03+dfsg2-8_all.deb ...
Unpacking cowsay (3.03+dfsg2-8) ...
Selecting previously unselected package libtext-charwidth-perl.
Preparing to unpack .../libtext-charwidth-perl_0.04-10build3_amd64.deb ...
Unpacking libtext-charwidth-perl (0.04-10build3) ...
Setting up libtext-charwidth-perl (0.04-10build3) ...
Setting up cowsay (3.03+dfsg2-8) ...
rc=0

$ dpkg -l cowsay libtext-charwidth-perl | grep ^ii
ii  cowsay                 3.03+dfsg2-8  all          configurable talking cow
ii  libtext-charwidth-perl 0.04-10build3 amd64        get display widths of characters on the terminal

$ /usr/games/cowsay offline install
 _________________
< offline install >
 -----------------
        \   ^__^
         \  (oo)\_______
            (__)\       )\/\
                ||----w |
                ||     ||

解读:dpkg -i 接多个 .deb 时会先全部解包、再按依赖顺序统一配置(先 Setting up 依赖、再主包),rc=0 一次通过。这是离线装机的标准姿势——比单个安装再 install -f 更可控。

4.2 -C 审计 + --configure -a 修复闭环

场景:模拟「升级被打断」的半配置现场,走一遍标准修复流程:

$ cd /root && apt-get download sl 2>&1 | tail -1
W: Download is performed unsandboxed as root as file '/root/sl_5.02-1_amd64.deb' couldn't be accessed by user '_apt'. - pkgAcquire::Run (13: Permission denied)

$ dpkg --unpack /root/sl_*.deb >/dev/null; echo unpack_rc=$?
unpack_rc=0

$ dpkg -C
The following packages have been unpacked but not yet configured.
They must be configured using dpkg --configure or the configure
menu option in dselect for them to work:
 sl                   Correct you if you type `sl' by mistake


$ dpkg --configure -a; echo rc=$?
Setting up sl (5.02-1) ...
rc=0

$ dpkg -C; echo rc=$?
rc=0

$ dpkg -l sl | tail -1
ii  sl             5.02-1       amd64        Correct you if you type `sl' by mistake

解读:dpkg -C 发现解包未配置的 sl → dpkg --configure -a 统一收尾(输出 Setting up sl)→ 复查 -C 干净、状态 ii。生产上升级中断后的巡检顺序:-C 查现场 → --configure -a 收尾 → apt-get install -f 补依赖 → 再 -C 复查。

4.3 rc 残留巡检与批量清理

场景:长期运行的服务器上 dpkg -l 会积累一批 rc 状态行(删了程序但配置文件还在),定期批量清除:

$ dpkg -l | grep '^rc'
rc  plocate                         1.1.15-1ubuntu2                         amd64        much faster locate

$ dpkg -l | grep '^rc' | awk '{print $2}' | xargs apt-get purge -y -o Dpkg::Use-Pty=0 2>&1 | tail -3
After this operation, 0 B of additional disk space will be used.
(Reading database ... 26320 files and directories currently installed.)
Purging configuration files for plocate (1.1.15-1ubuntu2) ...

$ dpkg -l | grep '^rc' | wc -l; echo rc=$?
0
rc=0

解读:grep '^rc' 揪出残留行,awk '{print $2}' 取包名列,交给 apt-get purge 批量清除配置文件,复查计数归 0。清 rc 残留前应确认这些包确实不再需要(rc 行的包程序已删,只是配置文件占着目录)。

五、注意事项与常见问题

Q1:dpkg -i 报 dependency problems - leaving unconfigured 怎么处理? 这是 dpkg「无依赖解析」的设计行为(3.3 节实测):包停在 iU 状态,rc=1。处理顺序:apt-get install -f -y 自动补依赖并完成配置;彻底断网时按 4.1 节把依赖包下齐后多包一次安装。不要用 --force-all 硬怼依赖,容易留下隐性损坏。

Q2:E: Could not get lock /var/lib/dpkg/lock-frontend. It is held by process NNNN (apt-get)? dpkg/apt 共用前端锁,同一时间只允许一个写操作进程。本文实测遇到(另一 apt-get 进程存活期间执行 dpkg -i 报 dpkg frontend lock was locked by another process with pid NNNN,rc=2)。正确做法:ps aux | grep apt 确认进程 18610 之类是否仍在跑包操作,等它结束;确认无进程后锁会自动释放。严禁直接删 /var/lib/dpkg/lock* 文件——dpkg 官方 FAQ 明确警告这可能损坏锁区与系统状态。

Q3:dpkg -l 里的 rc、iU、un 都是坏状态吗? 不是。rc 只是有配置文件残留(正常,可按 4.3 清理);un 是「未安装且未登记」,是大量从未安装包的正常形态;iU 是解包未配置的中间态,若长期存在才说明安装被打断,需要 3.4 节的修复流程。真正异常的是含大写字母的组合(如 iF 半配置、iH 半安装)和 R 错误位。

Q4:dpkg -r 之后,为什么有的包在 dpkg -l 里变成 rc,有的直接消失? 取决于包是否带 conffiles(配置文件)。有 conffiles 的(如 plocate 的 /etc/updatedb.conf,实测保留)→ 状态 rc;没有的(如 sl)→ 记录直接消失。所以「dpkg -l 里查不到」不一定是没装过,也可能是删干净了。

Q5:dpkg -c 包.deb | head 结尾多出一行 dpkg-deb: error: tar subprocess was killed by signal (Broken pipe)? 管道下游 head 提前关闭读端,tar 收到 SIGPIPE 被杀,dpkg-deb 如实报告。清单本身完整(head 只取了前 8 行)。介意的话改成重定向到文件再截取,或接受这条无害报错。

Q6:apt-get download 报 W: Download is performed unsandboxed as root ... couldn't be accessed by user '_apt'? 以 root 身份在 _apt 用户无权访问的目录(如 /root)下载时的提示,文件本身完好可用。规避方法:换普通用户可写的目录(如 /tmp)执行下载,或忽略该警告。

Q7:安装后运行程序报 command not found 或 Error opening terminal? 分两类:找不到命令先查 dpkg -L 包名 | grep bin——Ubuntu 把游戏类程序装在 /usr/games(不在默认 PATH,需全路径执行,如 /usr/games/sl);能找到但运行报 Error opening terminal: unknown. 的(如 sl),是程序需要终端环境(TERM 变量),无终端的脚本/重定向环境里属正常现象。

六、参考链接