If you have a system without a Wayland/X server (like a Raspberry Pi), the answers of andrewsomething and lovinglinux cannot be used. The answer of jarno limits the use case to PPAs only, although the question is of general interest. The scripts from Riccardo Murri and Graham Dunn are quite slow due to the repeated apt-cache policy
calls (like about 10 minutes runtime).
So this is my call solving the general case on a shell being a lot faster (like less than 10 seconds runtime)
apt list --installed 2> /dev/null \
| cut -d/ -f1 \
| parallel -n200 apt-cache policy \
| rg '^(\S+)[\s\S]+?\* (?:\S+\s+){3}(\S+)' -Uor '$1 $2'
apt list --installed
gets a list of all installed packages ignoring apt's message about possible future format changes with 2> /dev/null
and extracting only the package names with cut
by using /
as a delimiter with -d/
and returning the first field with -f1
.
Then, apt-cache policy
is used to get more information about all the packages. This could be executed with xargs, as apt-cache
expects its input as command line argument. As this is the remaining performance-critical part, GNU parallel
from package parallel
is used instead to run multiple apt-cache
processes in parallel looking up 200 packages with each using -n200
. Note, that xargs can run multiple commands in parallel, too, but synchronizes output on newline, which is not correct here in general.
Finally, apt-cache
's output is parsed with rg
from package ripgrep
which is a very fast and multiline capable grep
successor with -U
allowing to output two regular expression capture groups with -or '$1 $2'
. The regular expression captures the package name with ^(\S+)
, skips to the last star marking the installed repository with [\s\S]+?\*
, then skips three words with (?:\S+\s+){3}
and finally captures the repository with (\S+)
.