31

How can I get the list of all files under current directory along with their modification date and sorted by that date?

Now I know how to achieve that with find, stat and sort, but for some weird reason the stat is not installed on the box and it's unlikely that I can get it installed.

Any other option?

PS: gcc is not installed either

alex
  • 7,223

5 Answers5

28

My shortest method uses zsh:

print -rl -- **/*(.Om)

(add the D glob qualifiers if you also want to list the hidden files or the files in hidden directories).

If you have GNU find, make it print the file modification times and sort by that. I assume there are no newlines in file names.

find . -type f -printf '%T@ %p\n' | sort -k 1 -n | sed 's/^[^ ]* //'

If you have Perl (again, assuming no newlines in file names):

find . -type f -print |
perl -l -ne '
    $_{$_} = -M;  # store file age (mtime - now)
    END {
        $,="\n";
        print sort {$_{$b} <=> $_{$a}} keys %_;  # print by decreasing age
    }'

If you have Python (again, assuming no newlines in file names):

find . -type f -print |
python -c 'import os, sys; times = {}
for f in sys.stdin.readlines(): f = f[0:-1]; times[f] = os.stat(f).st_mtime
for f in sorted(times.iterkeys(), key=lambda f:times[f]): print f'

If you have SSH access to that server, mount the directory over sshfs on a better-equipped machine:

mkdir mnt
sshfs server:/path/to/directory mnt
zsh -c 'cd mnt && print -rl **/*(.Om)'
fusermount -u mnt

With only POSIX tools, it's a lot more complicated, because there's no good way to find the modification time of a file. The only standard way to retrieve a file's times is ls, and the output format is locale-dependent and hard to parse.

If you can write to the files, and you only care about regular files, and there are no newlines in file names, here's a horrible kludge: create hard links to all the files in a single directory, and sort them by modification time.

set -ef                       # disable globbing
IFS='
'                             # split $(foo) only at newlines
set -- $(find . -type f)      # set positional arguments to the file names
mkdir links.tmp
cd links.tmp
i=0 list=
for f; do                     # hard link the files to links.tmp/0, links.tmp/1, …
  ln "../$f" $i
  i=$(($i+1))
done
set +f
for f in $(ls -t [0-9]*); do  # for each file, in reverse mtime order:
  eval 'list="${'$i'}         # prepend the file name to $list
$list"'
done
printf %s "$list"             # print the output
rm -f [0-9]*                  # clean up
cd ..
rmdir links.tmp
  • The easiest way is probably find . -print | xargs -n99999 -s999999 ls -ltr. But that has the problem that (1) xargs may not allow -m greater than 512 or -s greater than 5120, and (b) even if you can get around that, there's still a kernel-imposed maximum size of the combined argument list and environment. Most of your ideas (save the Perl and Python ones) have the same problem, which is why I specifically avoided building long command lines. – geekosaur Mar 14 '11 at 22:49
  • In particular, I regularly get "argument list too long" errors using zsh recursive globs in the general case. – geekosaur Mar 14 '11 at 22:51
  • @geekosaur: Only the last horrible kludge has a problem with long command lines. In zsh, you can do a lot with built-ins (e.g. print -rl **/*'s only limit is how much free memory you have), and beyond that there's zargs. Your proposal of find … | xargs … ls will sort correctly if xargs ends up invoking ls only once, and won't work if there are special characters in file names. – Gilles 'SO- stop being evil' Mar 14 '11 at 23:00
  • many thanks for extremely detailed answer with lot of options! :) – alex Mar 15 '11 at 06:35
  • And that’s why I love zsh. – Profpatsch Nov 08 '13 at 20:35
  • ⁺¹ for the assuming no newlines in file names, that's actually funny since nobody uses newlines in filenames, though theoretically these could be. – Hi-Angel May 13 '15 at 14:02
14

Assuming GNU find:

find . -printf '%T@ %c %p\n' | sort -k 1n,1 -k 7 | cut -d' ' -f2-

Change 1n,1 to 1nr,1 if you want the files listed most recent first.

If you don't have GNU find it becomes more difficult because ls's timestamp format varies so much (recently modified files have a different style of timestamp, for example).

geekosaur
  • 32,047
7

On a mac there is no -printf argument to find, but you can do this instead:

find . -print0 | xargs -0 -n 100 stat -f"%m %Sm %N" | sort -n|awk '{$1="";print}'

killdash9
  • 171
0

Ignoring hidden files — with nice & fast time stamp

Handles spaces in filenames well — not that you should use those!

$ find . -type f -not -path '*/\.*' -printf '%TY.%Tm.%Td %THh%TM %Ta %p\n' |sort -nr |head -n 10

2017.01.25 18h23 Wed ./indenting/Shifting blocks visually.mht
2016.12.11 12h33 Sun ./tabs/Converting tabs to spaces.mht
2016.12.02 01h46 Fri ./advocacy/2016.Vim or Emacs - Which text editor do you prefer?.mht
2016.11.09 17h05 Wed ./Word count - Vim Tips Wiki.mht

More find galore can be found by following the link.

0

Speaking generally of finding files by date (this won't work for the original poster, but I ended up here so I thought others might as well). In my use case I want to list the files for the purpose of reviewing them before deletion.

With findutils 4.6.0 I like:

find . -type f -mtime +270 -exec ls -laFGht --time-style=iso {} \+

The above command finds files (-type f), in the current working directory (.) that were modified more than 270 days ago (-mtime +270 Also -mtime 0 will produce the last 24 hours, and -mtime -5 shows the last 5 days). It then uses ls to list them by date, newest first (-exec ls -laFGht --time-style=iso {} \+)

Here's a sample of the output:

-rwxrwx---+ 1 user1 208M 2018-07-16  ./filename.bak*
-rwxrwx---+ 1 user1  702 2018-07-15  ./filename.ldf*
-rwxrwx---+ 1 user1 208M 2018-07-15  ./filename.bak*

The great thing about this is that once the list has been reviewed, its a simple matter to replace the list part with the find delete command:

find . -type f -mtime +270 -delete
Steph
  • 1