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
for dir in */*; do # loop over the directories
( # run in a subshell ...
cd "$dir" # ... so we don't have to cd back
files=(*) # store the filenames in a zero-indexed array
for index in "${!files[@]}"; do
file=${files[$index]}
ext=${file##*.}
newname=$(printf "%02d.%s" $((index+1)) "$ext")
mv "$file" "$newname"
done
)
done
Suppose you have a file with no extension. In that case it will have the same name except with leading numbers (e.g. my_file
=> 05.my_file
)
All non-hidden directory entries will be renamed, including directories.
Best Answer
In zsh it's as easy as
The glob qualifier
om
causes the matches to be sorted by modification time (newest first), andN
forces the array to be empty if there is no match (instead of causing an error).In other shells such as bash, there's no good way of sorting by time. You can use
ls -t
, but that can break because the output is ambiguous. If you know that your file names only contain characters thatls
considers to be printable, and that are not whitespace or\[*?
, then you can use a command substitution:You can make this more robust by protecting wildcard characters and horizontal whitespace, but newlines and nonprintable characters remain a problem.
Except in zsh, don't forget the double quotes when refering to the value of a variable, as well as
--
to protect files whose name begins with a dash and would otherwise be interpreted as options, e.g.rm -- "${array[0]}"