0

What is the find script to sort a subtree into files with the most recent modification time? Also preserving their path name?

I suspect it has something to do with find, ls, awk and sort. I haven't seen this question asked here. Preferably using only BSD style tools in the answer vs Linux.

1
  • Linux has no command, it's just a kernel. Would using GNU commands such as GNU find which is available for most systems including BSD ones (even if not always installed by default) be an option? Commented Jan 26 at 18:07

2 Answers 2

0

The findcommand has a -printf directive whose format options include a %T@ operator, which outputs the last-modified time since the epoch (other formats are possible). That can be sorted with the reverse numeric options, and then stripped with sed or awk.

If there is any possibility that some of your filenames may include a newline, you need to use the \0character to end your format, and use the -zoption in sort.

find ./Toshiba -type f -printf '%T@|%p\n' | sort -rn | sed -e 's/^[^|]*|//'

4
  • -printf is a non-standard extension of the GNU implementation of find, typically not found in the find found of BSD. sort -z is also a GNU extension, though as sort on BSDs used to be GNU sort (and on some still are), it's also supported there. Commented Jan 26 at 17:26
  • @StéphaneChazelas My bad. The BSD -ls option seems to provide the mod time, but the format is not specified and is unlikely to be conveniently parseable or sortable. Commented Jan 26 at 17:40
  • -printf isn't working on MacOS, which is closer to BSD Commented Jan 26 at 18:22
  • @atod Sorry -- I'm not in a position to test any BSD solution. My go-to would be to pipe the findto Awk, parse either the -lsor the plain -printoutput, run stat and sortas needed. Very unstable, non-portable, clumsy and slow, so a last resort. Commented Jan 27 at 9:44
0

find doesn't sort files¹.

ls can sort files by modification time, but if you do:

find subtree -exec ls -td {} +

And the list of files is too big to fit in the limit of the size of arguments+environment passed to a command, that will end up running several ls invocations which will be sorted independantly.

Easiest would be to use zsh whose globs can do most of what find can do and sort the list based on various criteria including last modification time:

print -rC1 -- subtree/**/*(NDom)

Or to pass to another command in that order:

print -rNC1 -- subtree/**/*(NDom) | xargs -r0 another command

Or just:

another command subtree/**/*(NDom)

If the list is small enough to fit in one external command invocation.

Change o to O to reverse the order (and have the oldest files first).

Or you could use perl to do the sorting:

find subtree -print0 | perl -MTime::HiRes=lstat -l -0e '
  print $_->[1] for sort {$b->[0] <=> $a->[0]} map {[(lstat$_)[9], $_]} <>'

Or to pass to another command:

find subtree -print0 | perl -MTime::HiRes=lstat -0le '
  print $_->[1] for sort {$b->[0] <=> $a->[0]} map {[(lstat$_)[9], $_]} <>' |
  xargs -r0 another command

Swap $a and $b to reverse the order.

If you can guarantee that no file path contains newline directories (though in practice no Unix-like system that I know forbids those), you can use the BSD stat command to print the timestamp with subsecond precision:

find subtree -exec stat -f %Fm%t%N {} + |
  LC_ALL=C sort -rn |
  cut -f2-

To pass to another command:

find subtree -exec stat -f %Fm%t%N {} + |
  LC_ALL=C sort -rn |
  cut -f2- |
  LC_ALL=C tr '\n' '\0' |
  xargs -r0 another command

Remove the -r option of sort to reverse the order.

Beware files that cannot be lstat()ed will be skipped.

If, as suggested by your other recent questions, your "BSD" is actually Solaris 10 (which you had already confused for a BSD), your best bet among those is probably the zsh approaches (as Solaris 10 does come with zsh; a 20 year old version but recent enough for this purpose), though note that the xargs in Solaris 10 doesn't support -r nor -0 (which have only been added to the 2024 edition of the POSIX standard).

From another shell:

zsh -c 'print -rC1 -- subtree/**/*(NDom)'
zsh -c 'another command subtree/**/*(NDom)'

Or if the list is too large, use zsh's zargs autoloadable function rather than the very limited xargs from Solaris:

zsh -c 'autoload zargs; zargs subtree/**/*(NDom) -- another command'

¹ Well strictly speaking, you may find that some find implementations internally sort directory entries by inode number in an attempt to reduce the number of head seeks on rotational hard drives.

4
  • Hi I'm hoping to accomplish it using ksh88 and BSD programs. Tried find subtree -exec stat -f %Fm%t%N {} + | LC_ALL=C sort -rn | cut -f2-. I'm seeing dates out of order. Commented Jan 26 at 18:30
  • @atod. I'm curious as to what BSD would have ksh88. ksh88 code was never released, so I would be surprised to find it on a BSD. Note that Solaris is not a BSD. It changed named from SunOS in the 80s when it switched from BSD to SysV Commented Jan 26 at 18:46
  • ksh93 is fine to use Commented Jan 26 at 18:50
  • @atod, does your build of ksh93 has ls builtin (does builtin ls return an error)? Commented Jan 26 at 18:51

You must log in to answer this question.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.