I know that I can sort the output of ls by time with the -t option.

I know that when I have so many files that they don't fit in a single ls invocation, I can normally use xargs (or find ... -exec ... {} +) to let ls be called multiple times.

How can I combine the two? I have more files than fit on the command-line, and wish to list them sorted by time. find . -type f -exec ls -t {} + doesn't work, because supposing exactly 1000 file names fit on the command-line, and 3000 files are present, this will run ls -t [first 1000 files]; ls -t [second 1000 files]; ls -t [last 1000 files], where the last 1000 files find sees may well have a modification time before any of the first 1000. It doesn't seem like anything involving xargs or equivalent has any chance whatsoever of working, it seems like that approach is fundamentally flawed, but I cannot find a way that does work.


ls -t on its own will list all files in the current directory with that sorting, without ever needing to list them on the command line at all.

If you need the recursion behaviour of find, or to do some other tests on the files, you can have find generate timestamped entries, either through stat or through GNU find's -printf option and sort it. Something like:

find . -type f -printf '%T@ %p\0' | sort -zn

-printf '%T@ %p\0' generates null-separated Unix timestamp (%T@)-filename (%p) pairs. sort -z is also a non-standard GNU extension, which uses null-delimited records to be filename-safe. The sort option is supported in most of the BSDs too, but -printf is GNU-only as far as I know.

You can cut that output back into filenames only, or any other format you like.

| improve this answer | |
  • Thanks! I didn't know -printf was this powerful. According to the documentation, %A@ is the access time. I'm looking for the mtime, so I'd need %T@. – hvd Aug 11 '14 at 7:43
  • It's once again astonishing what seemingly simple tools have in stock... – countermode Aug 11 '14 at 8:07
  • What command would you use to cut each of the NUL delimited strings after the first space? – kasperd Aug 11 '14 at 17:27
  • 1
    @kasperd, (GNU) sed -z 's/^[^ ]* //' or tr '\0\n' '\n\0' | cut -d ' ' -f2- | tr '\0\n' '\n\0' – Stéphane Chazelas Aug 12 '14 at 8:38

You can use find with perl Posixly:

$ find ! -name . -prune -print | perl -lne '
    $h{$_} = -M;
    END { print for sort {$h{$a} <=> $h{$b}} keys %h }

This assume that you don't have newline character in your filename.

To handle newline, you can:

$ find ! -name . -prune -exec printf "%s\0" {} + | perl -0lne ...

or use perl -le then split /\0/,<> in your code.

| improve this answer | |
  • Oh, nice. Although I don't have newline characters in file names, it can be made to handle them (and still valid for any POSIX find, I think, although the use of perl is inherently somewhat non-portable) by adjusting it to find ... -exec printf "%s\0" {} + | perl -0lne '...', optionally also modifying the perl bit to print newlines instead of NULs, or act on the files directly. – hvd Aug 11 '14 at 10:14
  • @hvd: -printf is not POSIX. perl is more portable, always. – cuonglm Aug 11 '14 at 10:16
  • I know, that's why I didn't use -printf. I used -exec in combination with POSIX's printf utility. – hvd Aug 11 '14 at 10:18
  • I was referring to my comment here, where I didn't use %T@. %T@ won't work with the printf utility, only with find's non-portable -printf option. – hvd Aug 11 '14 at 10:21
  • @hvd: If you use that, you acn use perl -le, then split /\0/,<> to get list of all files. – cuonglm Aug 11 '14 at 10:22

In zsh:

print -rl -- *(om)

The glob qualifier om sorts files by modification time (reverse chronological order, i.e. newest first, like ls -t). Use Om to get the opposite order (chronological order, like ls -tr). Make this *(Dom) to include dot files.

You can of course vary the pattern, for example **/*(Dom) to recurse into subdirectories. If you want to use multiple patterns, put them in parentheses and use the | operator to separate them, e.g. *.(png|jpg)(om).

Since print is a shell builtin, this command is not affected by the maximum command line length limit. If you want to call an external command to act on the files in batches and in order, you'll run into the length limit. You can use printf '%s\0' *(om) | xargs -0 somecommand to feed the files to xargs if your xargs supports null-separated input (Linux, *BSD), or zsh's own zargs:

autoload -U zargs
zargs -r -e '' -- *(om) '' somecommand

To process one file at a time, use a simple loop, which zsh lets you abbreviate (csh-style):

for x (*(om)) somecommand
| improve this answer | |
  • This ignores the fact that I was searching files in all subdirectories. Does zsh have anything like bash's globstar option to address that? A quick search suggests **/*, but I don't have zsh installed to verify. – hvd Aug 12 '14 at 5:15
  • 1
    @hvd, the **/* syntax was invented by zsh in the early 90s, decades before bash copied (a subset of) it. See The result of ls * , ls ** and ls *** for more historical details of that globbing operator. zsh 1991, ksh93 2003, fish 2005, bash 2009, tcsh 2010. – Stéphane Chazelas Aug 12 '14 at 6:26

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.