Aptitude search ?narrow vs ?and

aptitude

What is the difference between the ?narrow and ?and options of aptitude search? For example, why would aptitude search "?and(?installed,?origin(backports))" return results on my system, but aptitude search "?narrow(?installed,?origin(backports))" return nothing?

Note that running apt-cache policy on the packages returned by the ?and version shows that versions installed are not from backports, so the ?narrow result is the correct one. But I've run into similar unintuitive results before which have been solved by using ?narrow after some googling, but have never understood why.

Best Answer

Short answer

The first command

aptitude search '?and(?installed,?origin(backports))'

finds packages that are installed and have a backport available, but the backport is not necessarily installed. (Maybe the backport is installed, maybe it isn't.)

By contrast, the second command

aptitude search '?narrow(?installed,?origin(backports))'

finds package that are installed, and the currently installed version is from a backport, i.e. the backports that are actually installed. This is a more restrictive search, because the set of installed backports is a subset of available backports. You can think of it like this:

  • all packages

    • installed packages

      • installed packages with a backport available (first command)

        • installed backports (second command)

On your system, the first command returns results but the second returns nothing. This means that you have installed packages with backports available, but evidently none of those backports are currently installed.

Long answer

It has to do with aptitude's distinction between matching the package and matching the package version. From the documentation:

There is a subtle, but important, distinction between matching a pattern against a package, and matching it against all the versions of that package. When a pattern is matched against a package, each of its terms is matched against the package, and so each term will match if any version of the package matches. In contrast, when a pattern is matched against each version of a package, it will successfully match if it matches when all its terms are matched against the same version of the package.

For example: suppose that version 3.0-1 of the package aardvark is installed, but that version 4.0-1 is available. Then the search expression ?version(4\.0-1)?installed matches aardvark, because ?version(4\.0-1) matches against version 4.0-1 of aardvark, while ?installed matches against version 3.0-1. On the other hand, this expression does not match against all the versions of aardvark, because no single version is installed and also has a version number of 4.0-1.

The documentation for ?and reads:

?and(pattern1, pattern2), pattern1 pattern2

Matches packages that match both pattern1 and pattern2.

Note that this matches packages, not single versions. So this query:

aptitude search '?and(?installed, ?origin(backports))'

gets a list of all the package versions that are installed, then a list of all the package versions with an origin matching the regular expression backports, and then returns the packages that appear in both lists.

On the other hand, the documentation for ?narrow reads:

?narrow(filter, pattern), ~S filter pattern

Select packages for which a single version matches both filter and pattern.

So that's why this query only shows packages where the single installed version has an origin that matches backports:

aptitude search '?narrow(?installed, ?origin(backports))'

There is a related discussion for the ?any-version function:

?any-version(pattern)

Matches a package if any one of its versions matches the enclosed pattern.

Note: This term is closely related to ?narrow. In fact, ?any-version(pattern1 pattern2) is exactly the same as ?narrow(pattern1, pattern2).

Note: To be precise, as with any other pattern, it is not packages but versions of the packages which are matched. For aptitude search and other uses it does not make much difference, but aptitude versions will only show the versions that match, not all versions of the package for which any version matches.

By running aptitude versions instead of aptitude search, we find that these queries all give the same result:

aptitude versions '?and(?installed, ?origin(backports))'
aptitude versions '?installed?origin(backports)'
aptitude versions '?narrow(?installed, ?origin(backports))'

Whew! If you find the query language for aptitude confusing (as I do), you may prefer to use a different approach, such as Python's bindings to libapt. Rather than matching on version strings, you can check the origin string directly, like this:

import apt

apt_cache = apt.Cache()

for pkg in apt_cache:
    if pkg.is_installed:
        for pkg_origin in pkg.installed.origins:
            if pkg_origin.origin == 'Debian Backports':
                print(pkg.name)
Related Question