Bash – Sort Directories by Last Modified Content Recursively

bashdirectorynautilustimestamps

I have a bunch of directories at the same level and would like to sort them according to the last modified date of the content (recursive) inside them.
However, in nautilus, it looks like the directories' "last modified date" are only updated if new files are created inside.

Is there anyway to show the recursive "last modified date" of these directories?


Edit:
I only needed to know the date to the nearest minute. So I've adopted Stéphane Chazelas's solution with minor modifications to reduce the clutter:

find . -mindepth 2 -type f -printf '%TF %TH:%TM/%P\0' |
LC_ALL=C sort -zt/ -k2,2 -k1,1r |
LC_ALL=C sort -t/ -zmsuk2,2 |
LC_ALL=C sort -z |
cut -zd/ -f1,2 | tr '\0/' '\n\t'

Best Answer

The last modification time of a directory (think like phone directory, not folder) is the time it was last modified, like when an entry was removed, added or edited in that directory.

To find out the newest regular file recursively in it, you would need to read the contents of that directory and every directory within and for each file, check the file's modification time. That's a costly thing to do, I wouldn't expect any file manager application to do it.

You could however script it.

With the GNU implementations of find and sort (and any Bourne-like shell), you could do:

TZ=UTC0 find . -mindepth 2 -type f -printf '%TFZ%TT/%P\0' |
  LC_ALL=C sort -zt/ -k2,2 -k1,1r |
  LC_ALL=C sort -t/ -zmsuk2,2 |
  LC_ALL=C sort -z |
  tr '\0' '\n'

Which would give something like:

2020-02-08Z19:17:22.3588966190/Scripts/.distfiles
2020-02-09Z09:25:37.5336986350/StartupFiles/zshrc
2020-07-26Z20:33:17.7263164070/Misc/vcs_info-examples
2020-07-26Z20:33:17.7463157170/Util/ztst-syntax.vim
2020-08-22Z18:06:17.9773156630/Functions/VCS_Info
2020-08-30Z11:11:00.5701005930/autom4te.cache/requests
2020-08-30Z11:11:31.5245491550/Config/defs.mk
2020-08-30Z11:11:31.6085449480/Etc/Makefile
2020-08-30Z11:12:10.9305773600/INSTALL.d/share/zsh/5.8.0.2-dev/help
2020-10-22Z05:17:15.3808945480/Completion/Base/Utility
2020-10-22Z05:17:15.3928938520/Doc/Zsh/zle.yo
2020-10-22Z05:17:15.3968936190/Src/zsh.h
2020-10-22Z05:17:15.3968936190/Test/D02glob.ztst
2020-10-22Z05:17:15.4168924590/.git/logs/refs/heads/master

That is, giving the newest regular file in each directory with its timestamp. Directories without regular files in them are not shown.

To only see the list of directories, insert a cut -zd/ -f2 | before the tr command.

For a prettier output like in the zsh approach, you could replace the tr command with:

LC_ALL=C gawk -v RS='\0' -F / '{
  dir = $2; mtime = $1
  sub("[^/]*/[^/]*/", "")
  printf "%-20s %s (%s)\n", dir, mtime, $0}'

While we're at using gawk, we could also tell find to print the timestamp as a fractional Unix epoch time and gawk reformat it in local time:

find . -mindepth 2 -type f -printf '%T@/%P\0' |
  LC_ALL=C sort -zt/ -k2,2 -k1,1rn |
  LC_ALL=C sort -t/ -zmsuk2,2 |
  LC_ALL=C sort -zn |
  LC_ALL=C gawk -v RS='\0' -F / '{
    dir = $2; split($1, mtime, ".")
    sub("[^/]*/", "")
    printf "%-20s %s (%s)\n", dir, strftime("%FT%T." mtime[2] "%z", mtime[1]), $0}'

Which would give an output like:

cross-build          2019-12-02T13:48:33.0505299150+0000 (cross-build/x86-beos.cache)
m4                   2019-12-02T13:48:33.4615093990+0000 (m4/xsize.m4)
autom4te.cache       2019-12-02T13:50:48.8897482560+0000 (autom4te.cache/requests)
CWRU                 2020-08-09T17:17:21.4712835520+0100 (CWRU/CWRU.chlog)
include              2020-08-09T17:17:21.5872807740+0100 (include/posixtime.h)
tests                2020-08-09T17:17:21.8392747400+0100 (tests/type.right)
.git                 2020-08-09T17:17:21.8472745490+0100 (.git/index)
doc                  2020-08-09T17:35:35.1638603570+0100 (doc/Makefile)
po                   2020-08-09T17:35:35.3758514290+0100 (po/Makefile)
support              2020-08-09T17:35:36.7037954930+0100 (support/man2html)
lib                  2020-08-09T17:35:42.3755564970+0100 (lib/readline/libhistory.a)
builtins             2020-08-09T17:35:42.5035511020+0100 (builtins/libbuiltins.a)
examples             2020-08-09T17:35:47.1513551370+0100 (examples/loadables/cut)
INSTALL.d            2020-08-09T17:35:47.3993446790+0100 (INSTALL.d/lib/bash/cut)