27

I can generate output sorted by date in "normal order" with the following command:

ls -A -lR | grep "^-" | sed "s/.\{43\}/&\n/" | grep -v "^-" | while read ONELINE; do if echo $ONELINE | cut -d " " -f3 | grep -o '[0-9][0-9]:[0-9][0-9]' > /dev/null 2>&1; then echo $ONELINE | sed "s/.\{7\}/&$(date +%Y) /"; else echo $ONELINE; fi; done

Result:

Jan 23 2011 10:42 SOMETHING 2007.12.20.avi
Jun 26 2009 SOMETHING 2009.06.25.avi
Feb 12 2010 SOMETHING 2010.02.11.avi
Jan 29 2011 09:17 SOMETHING 2011.01.27.avi
Feb 11 2011 20:06 SOMETHING 2011.02.10.avi
Feb 27 2011 23:05 SOMETHING 2011.02.24.avi

How could I get the output where the newest file is at the top, like this:

Feb 27 2011 23:05 SOMETHING 2011.02.24.avi
Feb 11 2011 20:06 SOMETHING 2011.02.10.avi
Jan 29 2011 09:17 SOMETHING 2011.01.27.avi
Jan 23 2011 10:42 SOMETHING 2007.12.20.avi
Feb 12 2010 SOMETHING 2010.02.11.avi
Jun 26 2009 SOMETHING 2009.06.25.avi
AdminBee
  • 22,803
LanceBaynes
  • 40,135
  • 97
  • 255
  • 351

7 Answers7

31

On most unices, ls has a -t option. From the man page of my Debian box:

-t sort by modification time

Try the following command:

$ ls -lt

rahmu
  • 20,023
Alexander Pogrebnyak
  • 1,095
  • 1
  • 11
  • 16
20

Is that a text file you're trying to sort, or are you trying to view a directory listing by date? For the latter, use the -t flag to ls. For the former, see in particular the -M flag to GNU sort, which sorts by three-letter month name abbreviations. The following command sorts by the 3rd, 1st, 2nd and 4th columns, treating the second sort key as a month name.

sort -k 3 -k 1M -k2 -k4 in.txt

Do consider always using Japanese/ISO style dates, always in the order YYYY-MM-DD-HH-MM-SS (i.e. most significant first, constant column width). This way sorting by date is identical to a lexicographic sort.

  • Don't forget the leading zeroes on that date format suggestion. This is the format I use whenever I format the string myself. – stolenmoment May 26 '18 at 09:39
  • @stolenmoment You can use spaces instead of zeros if you don't mind spaces, the important point is a constant column with. 2018  5 26 works just as well as 2018 05 26 (as long as you don't go through markup that merges consecutive spaces…), what doesn't work well is 2018 5 26. – Gilles 'SO- stop being evil' May 26 '18 at 10:39
  • Yeah, I reflexively avoid spaces in filenames, I'm sure you can guess why. – stolenmoment May 26 '18 at 11:34
7

I’m not sure if the functionality was in the ls command when the original question was asked, but now you can use the option --full-time.

e.g.,

ls -t --full-time 

will sort with oldest time at the bottom; or if you use:

ls -tr --full-time

then it will reverse the order.

  • The question is about how to sort file by modification date/time. The -t option answers that question completely; --full-time isn’t really relevant. – G-Man Says 'Reinstate Monica' Feb 17 '17 at 21:05
  • The only thing -t option doesn't give you the year for files modified within six months where as --full-time gives you exactly that. You can also use time-style to format a date format to you liking. e.g ls -ltr --time-style+%Y-%m-%d\ %H:%M:%S – Bevan Thomas Feb 20 '17 at 08:35
  • I’m not sure what your point is.  ls -t doesn’t give you mode and owner of the files, the host name and operating system version, or the price of tea in China, either.  ls -t doesn’t even display the modification date at all.  But the question isn’t about displaying file information, it’s about sorting files by modification date.  You are not contributing to the answer of the question; you are adding nice-to-know supplementary information. When you get 50 reputation points, you can post that sort of information as a comment. – G-Man Says 'Reinstate Monica' Feb 21 '17 at 00:52
  • Even though ls -t does not show other info(mode/owner/group), adding -l will do. I use ll -t and ll is a common alias ls -l which solves the problem. For me, it is confusing at first that for changes older than 6 months, the year columns shows year, while when it's <6 months old, it's showing hours; and --full-time solves that. For me it's valid answer. @G-ManSays'ReinstateMonica' – WesternGun Feb 14 '22 at 11:49
1

I like the approach of using sort posted in the answer by Gilles 'SO- stop being evil', but it's not quite all the way there. First, technically the OP wants reverse chronological order, not forward, and so r needs to be addd to the sort criterial.
So starting with sort -k 3r -k 1Mr -k 2r -k 4r se.txt that would give you

Feb 27 2011 23:05 SOMETHING 2011.02.24.avi
Feb 11 2011 20:06 SOMETHING 2011.02.10.avi
Jan 23 2011 10:42 SOMETHING 2007.12.20.avi
Jan 29 2011 09:17 SOMETHING 2011.01.27.avi
Feb 12 2010 SOMETHING 2010.02.11.avi
Jun 26 2009 SOMETHING 2009.06.25.avi

But look closely and you'll note Jan 23, 2011 and Jan 29, 2011 are in the wrong order. This is due to a subtle bug, or perhaps unclear documentation in sort. An individual sort KEYDEF such as -k2 means starting from field 2, not only on field 2. It is best to also specify the stopping field as well (e.g. -k2,2 means sorting from field 2 to field 2, i.e. only field 2).

And so

sort -k 3,3r -k 1,1Mr -k 2,2r -k 4,4r

yields the correct results. (I was able to get this to work by setting only some of the stop fields in the KEYDEFs, but I don't know which are necessary and why, so it seems best to specify them all. Well, except maybe the last one, but then in the OP input file there are some records with 4 fields and some with 5 so the 4th field is not always the final field)

  • 1
    It's not a bug, it's as clearly specified in the manuals of most sort implementations and in the POSIX specification. See also Sort based on the third column – Stéphane Chazelas Jan 18 '22 at 17:08
  • I read GNU and BSD man pages. It is not clear at all to me why the first sort in my post fails. (Ignoring the 2 records that are only 4 fields)., it sorts 1st time by year-field then time-field then other things; 2nd time, using month criteria, by month-field, then day field, then time-field, then other things; 3rd time by day-field, then year-field, then time field, then other things, 4th time by other things. My reading of man page is that the day fields (col 2) get correctly sorted in the 3rd pass, and nothing should happen in the 4th (col 4+) pass to knock them out of order. – Chip Grandits Jan 18 '22 at 19:33
1

This will list all files recursively with ls output and reverse sorted by modified time (mtime):

find -print0 -type f directory_path | xargs -0 ls -ltr

Peform the search more quickly with fd

fd -0 -t f directory_path | xargs -0 ls -ltr
adamency
  • 388
  • (s/--type/-type/) That only works if file paths don't contain whitespace nor quotes nor backslashes and if the list of files is small enough that ls is invoked only once. – Stéphane Chazelas Sep 15 '23 at 06:16
  • @StéphaneChazelas Answer improved to address special chars. And as for ls invocation, the argument length limit on the vast majority of systems is 2Mib which means that even for an average full path of 100 characters, we still got a 20000 files batch. I believe that will be sufficient for >99% of people coming here. – adamency Sep 16 '23 at 22:51
  • Should be find directory_path -type f -print0 | xargs -r0 ls -ltrd (wrong order of arguments for find; without -r, you'd end up with the current directory listed if find found no regular (-type f) files; -d not strictly necessary as ls should not be passed any directory files, but good habit to have when passing file lists to ls) – Stéphane Chazelas Sep 19 '23 at 16:33
0

With the standard -t option, ls will sort files (given as arguments or found in the contents of directories passed argument) by modification time from newest to oldest.

With the -R option, ls enters a recursive mode, but it still lists the contents of each directory separately, so while each directory listing will be listed by modification time, you won't get a notion of the relative time of files between different directories. The files will also not be listed with their full path.

To list files recursively with their full paths, ordered by time, you'd need to pass all of them by path to ls. You could do it with:

LC_ALL=C find . -name . -o -name '.*' -prune -o -exec ls -ltd {} +

(-name . -o -name '.*' -prune -o with LC_ALL=C to exclude hidden files, like ls does by default without -a/-A when it lists the contents of directories itself)

But that will break as soon as there are so many files that find needs to call ls several times to work around the execve() limit on the size or argument+environment that can be passed to a command, where you'll end up with several independent sorted lists.

To address that, you'd need to sort the list outside of ls and pass the list to those several invocations of ls -ld. With the zsh shell and a xargs that supports the -0 and -r option, that can be done with:

print -rNC1 -- **/*(Nom) | xargs -r0 ls -ltd --

If your ls supports the non-standard -U option to skip sorting, you can use that in place of -t as zsh has already sorted the list with its om (order by modification time) glob qualifier. Add the D glob qualifier if you want hidden files to be included.

With the GNU implementations of find, ls, cut and sort, you can also do:

LC_ALL=C find . -name . -o -name '.*' -prune -o -printf '%T@\t%P\0' |
  sort -rzn |
  cut -zf2- |
  xargs -r0 ls -lUd --
-4

recursive (R) and pause (|more)

ls -ltR |more
techraf
  • 5,941