thiru thiru - 4 months ago 10
Bash Question

find the latest modified file and exec

I want to find out the latest built rpm in a directory and then exec something on it. Something similar to below.

/bin/ls -1t srcdir/*.rpm | head -1


But with the find command

find srcdir/*.rpm <get-only-the-most-recently-modified-file> -exec "<do-something>"

Answer

Two approaches -- one portable to non-GNU platforms, the other fast with large directories (to the extent allowed by the filesystem):


Portable

What?

BashFAQ #3: How can I sort or compare files based on some metadata attribute (newest / oldest modification time, size, etc)? is relevant here. To summarize:

latest=
for f in "$srcdir"/*.rpm; do
  [ "$f" -nt "$latest" ] && latest=$f
done

How?

[ "$a" -nt "$b" ] checks whether the file named in variable a is newer than that named in variable b in ksh-derived shells.


Fast

...to be clear, this is faster with very large directories, as opposed to faster in all cases. That said, it's also easily adapted to find (for instance) the 5 or 10 newest files, or all files except those 5 or 10 newest, which the other approach could not do nearly as effectively.

What?

If you have GNU tools (GNU find, GNU sort), consider the following:

{ read -r -d ' ' mtime && IFS= read -r -d '' filename; } \
  < <(find /directory -type f -iname "*.rpm" -printf '%T@ %p\0' | sort -z -r -n)

This will put your latest file's time (in seconds-since-epoch) in the shell variable mtime and the name of that file in filename. Thus, you can then operate on that file:

if [[ -e $filename ]]; then
  # do whatever arbitrary operations you're looking for on that resulting filename
  cp -- "$filename" /path/to/where/to/copy
fi

How?

To explain how this works:

find ... -printf '%T@ %p\0'

...emits contents in the format <epoch_mtime> <filename><NUL>, where epoch_mtime is the number of seconds since January 1st, 1970.

sort -z -r -n

...then sorts that output, expecting it to be NUL-delimited, on the numbers at the beginning.

{ read -r -d ' ' mtime && IFS= read -r -d '' filename; }

...reads content into the mtime variable up to the first space in the first line, and then reads forward to the first NUL into the filename variable.

Comments