Home / Alt manpages / dpkg-query(1)

  • dpkg-query(1)
  • User command
  • linux

Inspect Installed Packages with dpkg-query

Before you reach for a package manager to answer a question, check whether dpkg-query already has the answer sitting in its local database. It can tell you whether a package is installed, what version it is, which files it put down, which package owns a given path, and how to pull a stable field for a script, all without installing, removing or reconfiguring a thing.

Allow about fifteen minutes. You need a Debian or Ubuntu system with dpkg-query installed and a normal shell. The examples were checked with dpkg-query 1.22.6 and package version 1.22.6ubuntu6.6 on this machine. Output on another dpkg release or distribution can differ in package versions, descriptions and local file lists.

1. Confirm the program and package version

Start with the version check, an ordinary unprivileged command:

$ dpkg-query --version
Debian dpkg-query package management program query tool version 1.22.6 (amd64).

That line is the version of the query tool itself, not of every package it can report on. Keep the two separate when you write up a troubleshooting result.

Checkpoint

The command should exit successfully and print a version. If the shell says it is missing, restore dpkg through your distribution's normal package-management process; do not copy a binary over from an unrelated machine.

2. Check one package and its state

Use --show, or its short form -W, for a compact package-and-version result:

$ dpkg-query -W dpkg
dpkg	1.22.6ubuntu6.6

A line here only means a matching database entry exists, not necessarily that the package is currently installed. Query the status field when that distinction actually matters:

$ dpkg-query -W -f='${binary:Package}\t${Version}\t${db:Status-Status}\n' dpkg
dpkg	1.22.6ubuntu6.6	installed

binary:Package can carry an architecture qualifier where that is needed to disambiguate the name. The virtual db:Status-Status field pulls the status word straight out of the package's database entry.

For the full record, use --status:

$ dpkg-query --status dpkg | sed -n '1,10p'
Package: dpkg
Essential: yes
Status: install ok installed
Priority: required
Section: admin

Tip

Never parse the human-oriented output of -l in a script. It carries a heading and status columns built for a terminal. Use --show with an explicit format instead.

3. List matching packages without losing wildcards

--list, or -l, is fine for a human inventory. Quote any pattern so the shell does not expand it against filenames in the current directory first:

$ dpkg-query -l 'dpkg*'
Desired=Unknown/Install/Remove/Purge/Hold
| Status=Not/Inst/Conf-files/Unpacked/Half-configured/Half-installed/trig-aWait/Trig-pend
||/ Err?=(none)/Reinst-required
ii  dpkg       1.22.6ubuntu6.6  amd64  Debian package management system
ii  dpkg-dev   1.22.6ubuntu6.6  all    Debian package development tools

The three characters before a package name spell out desired action, package status and error flag. Here, ii means install is desired, the package is installed, and no error flag is set. An uppercase status or error letter is worth investigating on the spot, because it usually marks a package in a genuinely bad state.

With no pattern, --list lists known entries except ones marked purged. A pattern can also match real or virtual package names reached through dependency fields.

4. Find who owns a file

Use --search, or -S, when you have a path and need the package behind it:

$ dpkg-query -S /usr/bin/dpkg-query
dpkg: /usr/bin/dpkg-query

The search accepts shell wildcards. A plain value that does not start with *, ? or / is treated as a substring match, so an explicit absolute path is the safer habit when you mean exactly one file. The output can list more than one package, each followed by a colon and the matched path.

This is a database, not a filesystem scan: it will not surface files created later by maintainer scripts, and it will not report alternatives. If the command finds nothing, check spelling, whether the path is generated at runtime, and whether a diversion is involved.

For a script that parses file ownership, fix the locale so diversion text does not get localised under you:

$ LC_ALL=C.UTF-8 LANGUAGE=C.UTF-8 dpkg-query -S /usr/bin/dpkg-query
dpkg: /usr/bin/dpkg-query

5. List a package's installed files

Use --listfiles, or -L, to list the paths recorded for one package:

$ dpkg-query -L dpkg | sed -n '1,8p'
/.
/bin
/bin/dpkg
/etc
/etc/cron.daily
/etc/cron.daily/dpkg
/etc/dpkg
/etc/dpkg/dpkg.cfg

Name more than one package and their file lists come back separated by a blank line, in the order you supplied them. Still a database query: it never checks the filesystem to confirm every listed path still exists, and it will not include extra files created by maintainer scripts or alternatives.

6. Read package control files

For control files installed under dpkg's administrative area, prefer --control-list and --control-show:

$ dpkg-query --control-list dpkg
postinst
postrm
md5sums
$ dpkg-query --control-show dpkg postinst | sed -n '1,6p'
#!/bin/sh
# See deb-postinst(5).

set -e

PROGNAME=dpkg

The first command lists the available control-file names; pass one to the second. The older --control-path option can expose the internal path but the manual marks it deprecated. Reach for it only in a narrowly understood recovery case, such as locating a damaging maintainer script that needs removing, and think through the consequences first.

Warning

These commands print to standard output. Redirecting that to a new file is safe; editing a control file in place, or running a maintainer script as a diagnostic, is not. Changing dpkg's internal data can leave package operations inconsistent and may need elevated access to fix.

7. Build a query a script can actually rely on

Use a format string with an explicit newline. The default --show format is package name, a tab, and version; a custom format makes both the fields and the separators visible in the script itself:

package='dpkg'
dpkg-query --show --showformat='${binary:Package}\t${Version}\t${db:Status-Status}\n' "$package"
status=$?
if [ "$status" -eq 0 ]; then
    printf '%s\n' 'package record found'
else
    printf 'dpkg-query failed with status %s\n' "$status" >&2
    exit "$status"
fi
  • Quote everything. Package patterns and variable expansions both need it.
  • Know your exit codes. A missing package or file gives status 1; invalid usage or a fatal database error gives status 2.
  • Read the fine print. For --control-path, --control-list and --control-show, the manual documents special fatal-error behaviour, so do not assume every failure means the same thing.
  • Skip sudo. Query operations should never need root. If a long result triggers a pager, DPKG_PAGER or PAGER controls it, and --no-pager stops one starting in a script or audit log.

Done means

  • You separate the tool's version from a package's version and state. They are not the same number.
  • You quote wildcard patterns passed to --list or --show.
  • You use formatted --show output for scripts instead of parsing the terminal table from --list.
  • You can name a file's owning package and list its files, knowing the limits of that database.
  • You reach for --control-list and --control-show before a deprecated internal path.
  • Every check here stayed read-only and unprivileged, unless a separate recovery step was explicitly reviewed first.