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

2
  • Can you upload a compiled binary and run it there? Mar 14, 2011 at 19:58
  • @imz: yes, that's another way to go. The find -printf action seems to be the easiest though.
    – alex
    Mar 15, 2011 at 10:09

5 Answers 5

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
6
  • 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, 2011 at 22:49
  • In particular, I regularly get "argument list too long" errors using zsh recursive globs in the general case.
    – geekosaur
    Mar 14, 2011 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. Mar 14, 2011 at 23:00
  • many thanks for extremely detailed answer with lot of options! :)
    – alex
    Mar 15, 2011 at 6:35
  • And that’s why I love zsh.
    – Profpatsch
    Nov 8, 2013 at 20:35
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).

3
  • Actually you can also change the date and time format with ls --time-style="..." - I'm not sure whether this is standard or GNU ls, likely GNU.
    – asoundmove
    Mar 14, 2011 at 22:22
  • Definitely GNU.
    – geekosaur
    Mar 14, 2011 at 22:28
  • @asoundmove: unfortunately, the box is too old (RedHat 7.2) and ls doesn't have that option.
    – alex
    Mar 15, 2011 at 6:19
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}'

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

You must log in to answer this question.

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