How to sort results of Find statement by date?

6,095

Solution 1

get file names only ... sorted by modification day

find + sort + cut approach:

find . -regex ".*/[0-9.]+" -printf "%T@ %f\n" | sort | cut -d' ' -f2

  • %T@ - File's last modification time, where @ is seconds since Jan. 1, 1970, 00:00 GMT, with fractional part

  • %f - File's name with any leading directories removed (only the last element)


To sort in descending order:

find . -regex ".*/[0-9.]+" -printf "%T@ %f\n" | sort -k1,1r | cut -d' ' -f2

Solution 2

Your method can be adapted to work in simple cases. The main problem you're facing is that you're passing input to ls, but ls doesn't take any input. ls takes command line arguments. So you need to pass the output of find as arguments to ls, with a command substitution. Also, in case a directory is matched, pass -d to ls to list the directory itself and not its contents.

OLDDATA=$(ls -td $(find . -regex ".*/[0-9.]+"))

Only in simple cases, because there are two restrictions:

  1. This relies on an unquoted command substitution (and so does the use of $OLDDATA afterwards). Therefore it assumes that the file names don't contain any special characters (whitespace or wildcard characters \[*?).
  2. Some versions of ls may mangle characters that are not printable in the current locale.
  3. If the total length of the file names is too long, you'll get an error. (Note that find … -exec and xargs cannot help here, since ls must run a single time to get the order of the file names right. All they could do is hide errors and produce output that is not correctly sorted — and mangle a few more characters, in the case of xargs.)

A robust, simple way of doing this is to use zsh. It has the ability to sort wildcard matches, thanks to glob qualifiers.

setopt extended_glob
OLDDATA=(**/[0-9.]##(om))
  • Since this doesn't call any other program, there is no length limit other than available memory, and no risk of file name mangling at any point.
  • The result is a list of strings (each string being a file name), not a string, so it goes into an array variable.
  • **/ traverses subdirectories recursively, avoiding the use of find.
  • ## means “one or more of the preceding” in zsh extended glob syntax, it's analogous to + in (extended) regex syntax.
  • (om) is a glob qualifier to sort files by modification time, like ls -t.

There is notoriously no simple way to do this robustly with POSIX tools or even with GNU tools and ksh.

Share:
6,095
harcotlupus
Author by

harcotlupus

Updated on September 18, 2022

Comments

  • harcotlupus
    harcotlupus almost 2 years

    Trying to find files that contains specific strings in name, but don't know how to sort output, in a way, that I will get file names only.

    I've tried

    OLDDATA=`find . -regex ".*/[0-9.]+" | ls -t`
    

    But ls -t is not working on find result but on whole directory

    edit: Result of this statement should be sorted by modification day directories. This regex suppose to match directories that contains only numbers and dots in name.

    • ricmarques
      ricmarques over 6 years
      May I suggest that you explain what you think that regular expression - ".*/[0-9.]+" - should match?
    • harcotlupus
      harcotlupus over 6 years
      This should match files that contains only numbers and dots in name
  • Angel Todorov
    Angel Todorov over 6 years
    The good ol' Schwartzian transform
  • harcotlupus
    harcotlupus over 6 years
    Is there option to sort them from the newest? It worked very well, but need dates DESC
  • RomanPerekhrest
    RomanPerekhrest over 6 years
    @harcotlupus, see my update
  • Carl Winbäck
    Carl Winbäck over 2 years
    If I run this on Debian 11 I will only get lines that look like this: 02265526.0