2

Let us assume a directory that contains pictures from different cameras:

DCIM1234.JPG
DCIM1235.JPG
DCIM1236.JPG

DSCN4120.JPG
DSCN4121.JPG
DSCN4122.JPG
DSCN4123.JPG

IMG5840.JPG
IMG5841.JPG
IMG5842.JPG
IMG5843.JPG

Sorting all these files by modification date across cameras is easy using ls -t. The problem is that most file systems have an accuracy of 1 second or more, so some pictures might have an identical timestamps, for instance when shooting bursts. In this case, ls -t might loose the natural order of the file, which is reflected in the names.

How to sort the files by modification time, while sorting by name the files that have an identical modification time?

2
  • 1
    Perhaps something as simple as stat -c "%Y %n" * | sort -gk1,1r -gk2,2`? Assumes no special characters in the file names, doesn't find hidden files, etc.
    – doneal24
    Commented Apr 3, 2022 at 14:56
  • 1
    ... or perhaps (since no specific shell is tagged) zsh with the Om and on glob qualifiers ex. zsh -c 'print -rC1 *.JPG(NOmon)' Commented Apr 3, 2022 at 15:03

2 Answers 2

5

Generally, it's recommended to avoid parsing ls output. As doneal24 suggested above, stat is a better option.

$ stat -c "%Y/%n" *.JPG | sort -t/ -k1,1n -k2 | sed 's@^.*/@@'

From man stat:

The valid format sequences for files (without --file-system):
...
%n file name
%Y time of last data modification, seconds since Epoch

So stat -c "%Y/%n" *.JPG will get you the timestamp in seconds and the name of each file, separated by a /. For example:

1580845717/IMG5841.JPG

The output of that command is piped to sort -t/ -k1,1n -k2, which sorts first by the first column, numerically (the timestamp), and then by the second column. Columns are separated by / (-t/).

Finally, the output of the sort command is piped to sed, which removes all character up to and including the first / (the chosen delimiter). The result in the list of filenames in the order you wanted (with the "newest" files listed last).

2
  • Better than my answer, nice job!
    – acjca2
    Commented Apr 3, 2022 at 15:28
  • That sounds good. A more generic solution would be: find . -type f -print0 | xargs -0 stat -c "%Y/%n" | sort -t/ -k1,1n -k2 | sed 's@^.*/@@'
    – ocroquette
    Commented Apr 4, 2022 at 18:42
0

Look into the command sort combined with formatting the ls date output. Something like this should be a good starting point:

TIME_STYLE=+%s ls -l | sort -k6,6n -k7 | awk '{print $7}'

Based on the output of ls -l like this:

-rw-rw-r--. 1 ajc ajc  669 1626129161 commit-types.txt
-rw-r--r--  1 ajc ajc 3578 1638037524 emacsintro.org
-rw-r--r--  1 ajc ajc  170 1646399639 README.md

Explanation of each part of that command:

  1. TIME_STYLE formats the date column of ls -l. In this case, we're using the Unix epoch style of output with +%s
  2. sort -k6,6n -k7. Sort numerically on the 6th column of ls -l output, then sort by the 7th column using the default method which is alphabetical.
  3. awk '{print $7}'. Just shows the 7th column. If you wanted to show the 6th column, you'd need to use awk '{print $6 $7}' instead.

Here's the sort documentation and the file timestamp documentation.

0

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .