List packages on an apt based system by installation date

How can I list installed packages by installation date?

I need to do this on debian/ubuntu. Answers for other distributions would be nice as well.

I installed a lot of stuff to compile a certain piece of code, and I want to get a list of the packages that I had to install.

Asked By: Elazar Leibovich

||

RPM-based distributions like Red Hat are easy:

rpm -qa --last

On Debian and other dpkg-based distributions, your specific problem is easy too:

grep install /var/log/dpkg.log

Unless the log file has been rotated, in which case you should try:

grep install /var/log/dpkg.log /var/log/dpkg.log.1

In general, dpkg and apt don’t seem to track the installation date, going by the lack of any such field in the dpkg-query man page.

And eventually old /var/log/dpkg.log.* files will be deleted by log rotation, so that way isn’t guaranteed to give you the entire history of your system.

One suggestion that appears a few times (e.g. this thread) is to look at the /var/lib/dpkg/info directory.
The files there suggest you might try something like:

ls -t /var/lib/dpkg/info/*.list | sed -e 's/.list$//' | head -n 50

To answer your question about selections, here’s a first pass.

build list of packages by dates

$ find /var/lib/dpkg/info -name "*.list" -exec stat -c $'%nt%y' {} ; | 
    sed -e 's,/var/lib/dpkg/info/,,' -e 's,.listt,t,' | 
    sort > ~/dpkglist.dates

build list of installed packages

$ dpkg --get-selections | sed -ne '/tinstall$/{s/[[:space:]].*//;p}' | 
    sort > ~/dpkglist.selections

join the 2 lists

$ join -1 1 -2 1 -t $'t' ~/dpkglist.selections ~/dpkglist.dates 
    > ~/dpkglist.selectiondates

For some reason it’s not printing very many differences for me, so there might be a bug or an invalid assumption about what --get-selections means.

You can obviously limit the packages either by using find . -mtime -<days> or head -n <lines>, and change the output format as you like, e.g.

$ find /var/lib/dpkg/info -name "*.list" -mtime -4 | 
    sed -e 's,/var/lib/dpkg/info/,,' -e 's,.list$,,' | 
    sort > ~/dpkglist.recent

$ join -1 1 -2 1 -t $'t' ~/dpkglist.selections ~/dpkglist.recent 
    > ~/dpkglist.recentselections

to list only the selections that were installed (changed?) in the past 4 days.

You could probably also remove the sort commands after verifying the sort order used by dpkg --get-selections and make the find command more efficient.

Answered By: Mikel

Mikel has shown how to do this at the dpkg level. In particular, /var/lib/dpkg/info/$packagename.list is created when the package is installed (and not modified afterwards).

If you used the APT tools (which you presumably did since you’re concerned about automatically vs manually installed packages), there’s a history in /var/log/apt/history.log. As long as it hasn’t rotated away, it keeps track of all APT installations, upgrades and removals, with an annotation for packages marked as automatically installed. This is a fairly recent feature, introduced in APT 0.7.26, so in Debian it appeared in squeeze. In Ubuntu, 10.04 has history.log but the automatically-installed annotation is not present until 10.10.

The /var/log/apt/history.log file has an awkward format IMHO.

Start-Date: {date} {time}
Commandline: {command} {options …}
Install: {package (version)}, …, {package (version)},

End-Date: {date} {time}

I would have preferred a more log-file formatted record

{date}{time}{tab}{package}{tab}{version}{tab}{command}{options}n

or some XML showing not only a {package} but any {dependencies}.

As currently implemented, you can discover the information you seek but it requires some forensic processing to extract the details.

Answered By: Saint DanBert

Rough, but works :

for fillo in `ls -tr /var/lib/dpkg/info/*.list` ; 
    do basename ${fillo} | sed 's/.list$//g' ; 
done > forens.txt

ls -ltr /var/lib/dpkg/info/*.list > forentime.txt

for lint in `cat forens.txt` ; do 
    echo -n "[ ${lint} Installed ] : " ; 
    echo -n "`grep /${lint}.list forentime.txt | awk '{ print $6, $7, $8 }'` : " ; 
    ( ( grep -A3 " ${lint}$" /var/lib/apt/extended_states | 
        grep '^Auto' > /dev/null ) && echo "Auto" ) || echo "Manual" ; 
done > pkgdatetime.txt
Answered By: Dulantha

This works for me on a Debian system, I’m guessing the file format has changed since 2011. This system is pretty fresh so I wouldn’t expect this
to work on an older system, although that might just require unzipping the logs and using a glob to refer to all of them.

grep 'install ' /var/log/dpkg.log.1 | sort | cut -f1,2,4 -d' '

The first two fields in each line of the file /var/log/dpkg.log are the date and time. Note the trailing space with install in the grep part, this is because upgrades can trigger installs but if I understood correctly you wanted to know what was installed by users.

Answered By: Amias

Noting this because you mention that other distribution answers are welcome. rpm has a large set of output format tags, one of which is INSTALLTIME. (Using wget as an example)

rpm -qi wget --qf "%{NAME},%{INSTALLTIME}n" | tail -n 1
wget,1454014156

This can be formatted in a few ways. I use it in this way:

rpm -qi wget --qf "%{NAME},%{INSTALLTIME:date}n" | tail -n 1
wget,Thu 28 Jan 2016 03:49:16 PM EST

These two pages have a ton of great info on solving RPM metadata issues:

http://www.rpm.org/max-rpm/s1-rpm-query-parts.html

http://www.rpm.org/max-rpm/s1-rpm-query-handy-queries.html

Sorting this information would give you a working solution for your issue.

Answered By: Jonathan Swift

GNU/Linux Debian has no built-in tools for this problem, but all information about programs installed in the standard way is saved in files with program-name.list in the location /var/lib/dpkg/info/. But there is no information about manually installed programs there.


A long single-line solution:

for file_list in `ls -rt /var/lib/dpkg/info/*.list`; do 
    stat_result=$(stat --format=%y "$file_list"); 
    printf "%-50s %sn" $(basename $file_list .list) "$stat_result"; 
done

Explanation:

  1. ls -rt outputs files sorted by date modification in the reverse order, i.e. with the newest files at the end of the list.
  2. stat prints the file’s date in human readable form.
  3. printf displays the package name and the date of its last modification.
  4. The for loop as a whole prints package names and dates from oldest to newest.

Output example (truncated):

.........................................
gnome-system-log                            2016-09-17 16:31:58.000000000 +0300
libyelp0                                    2016-09-17 16:32:00.000000000 +0300
gnome-system-monitor                        2016-09-17 16:32:00.000000000 +0300
yelp-xsl                                    2016-09-17 16:32:01.000000000 +0300
yelp                                        2016-09-17 16:32:03.000000000 +0300
gnome-user-guide                            2016-09-17 16:32:18.000000000 +0300
libapache2-mod-dnssd                        2016-09-17 16:32:19.000000000 +0300
.........................................
linux-compiler-gcc-4.8-x86                  2017-02-26 20:11:02.800756429 +0200
linux-headers-3.16.0-4-amd64                2017-02-26 20:11:10.463446327 +0200
linux-headers-3.16.0-4-common               2017-02-26 20:11:17.414555037 +0200
linux-libc-dev:amd64                        2017-02-26 20:11:21.126184016 +0200
openssl                                     2017-02-26 20:11:22.094098618 +0200
unzip                                       2017-02-26 20:11:23.118013331 +0200
wireless-regdb                              2017-02-26 20:11:23.929949143 +0200
nodejs                                      2017-02-26 20:11:33.321424052 +0200
nasm                                        2017-02-28 16:41:17.013509727 +0200
librecode0:amd64                            2017-03-01 10:38:49.817962640 +0200
libuchardet0                                2017-03-01 10:41:10.860098788 +0200
tree                                        2017-03-04 14:32:12.251787763 +0200
libtar0                                     2017-03-07 09:51:46.609746789 +0200
libtar-dev                                  2017-03-07 09:51:47.129753987 +0200

The main defect of this solution.is that it’s not well-tested in production.

Answered By: PADYMKO

Here is the one-liner everyone wants and needs:

for x in $(ls -1t /var/log/dpkg.log*); do zcat -f $x |tac |grep -e " install " -e " upgrade "; done |awk -F ":a" '{print $1 " :a" $2}' |column -t

The result will show all (newly) installed and upgraded packages in chronological order.

The line explanation:

  • ls -1t – get all dpkg.log* file names in chronological order
  • zcat -fIF file is of gzip type then decompress it, ELSE just pass on the content.
  • tac – Reverse output of cat, line-by-line to makes sure we get the correct chronological order.
  • grep – Only check for installed or upgrade packages.
  • awk -F ':a' – Separate the architecture field from the package name
  • column -t – pretty print the columns separated by space

One would of course like to make an alias for this, but unfortunately it is not possible as awk depends on both single and double quotes. In that regard this is best put into a bash script and where the : separator is handled better for other architectures in the field column.

The output is:

2018-03-06  18:09:47  upgrade  libgomp1                     :armhf  6.3.0-18+rpi1                 6.3.0-18+rpi1+deb9u1
2018-03-05  15:56:23  install  mpg123                       :armhf  <none>                        1.23.8-1
2018-03-05  15:56:23  install  libout123-0                  :armhf  <none>                        1.23.8-1
2018-01-22  17:09:45  install  libmailtools-perl            :all    <none>                        2.18-1
2018-01-22  17:09:44  install  libnet-smtp-ssl-perl         :all    <none>                        1.04-1

Drawback:

  • As shown above, it only works on ARM architecture and need slight modification for the architecture field separator
  • Need to be put into a script for easy alias
  • Has not been tested across other *nix systems
Answered By: not2qubit

It is rough, but works as quickly as other solutions. Date format is yyyymmddhhmmss, meaning that a bit or reordering and format removal results in a number that can be sorted.

Many thanks to the other solutions, this list package names in order of installation that could be used in a built to make copy operating system.

find /var/lib/dpkg/info -name "*.list" -exec stat -c $'%nt%y' {} ; 
| sed -e 's,/var/lib/dpkg/info/,,' -e 's,.listt,t,' 
| sort | awk '{print $2$3" "$1}' | sed '0,/RE/s/-//' 
| sed '0,/RE/s/-//' | sed '0,/RE/s/://' | sed '0,/RE/s/://' 
| sed '0,/RE/s/\.//' | sed 's/:armhf//' | sort | awk '{print $2}'
Answered By: Alexander Cave

With rotated logfiles in apt you could:

zcat -f /var/log/dpkg.log* | grep " install " | less
Answered By: Gabriel Arellano

Ive achieved it on Kubuntu with the following commands:

  1. List the packages.
  2. read every standard input.
  3. store the full path (path | created date) with the format "%n|%y" into a variable.
  4. store the name of the file into a variable
  5. if both previous commands were executed successfully, values will be printed in a single line.
ls /var/lib/dpkg/info/*.list |while read xfile; do wpath=$(stat -c '%n|%y' ${xfile}) && wfile=$(basename ${xfile}) && printf "${wfile}|${wpath}n" ;done

Best regards,

Answered By: DarkPenguin15

Put the below in your shell rc file.

# list date and name of installation packages
function lspkg_func {
    # Save the installation text to a variable.
    local installation_text=$(grep " install " /var/log/dpkg.log*)
    for i in $(dpkg-query -f='${Package}n' -W); do
        local pkg_date=$(echo "$installation_text" | grep " install $i:" | sort -n | tail -n1 | cut -d' ' -f 1,2)
        echo "${pkg_date:-1971-01-01 00:00:00}t$i"         # if pkg_date is null, use `1970-01-01 00:00:00` instead.
    done
}

# sort the result of `lspkg_func` by date descendingly
alias lspkgs='lspkg_func | sort -nr | less -i'

The output of lspkgs command.

2022-09-09 20:49:38     aptitude
2022-09-09 20:49:36     libxapian30
2022-09-09 20:49:31     libcwidget4
2022-09-09 20:49:29     libsigc++-2.0-0v5
2022-09-09 20:49:26     libboost-iostreams1.74.0
2022-09-09 20:49:16     aptitude-common
2022-09-09 17:35:17     nodejs-doc
2022-09-09 17:35:12     nodejs
Answered By: Johan Chane
Categories: Answers Tags: , ,
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.