First I had
for file in `ls -t dir` ; do
#blah
done
but files with spaces are split into two iterations.
I've found tons of variations on this that fix the spaces issue, but then leaves some date info in the $file
variable.
Edit: to show one such variation:
for file in `find . -printf "%T@ %Tc %p\n" | sort -n` ; do
#blah
done
The problem with this is that all the time info is still in-place within the $file
variable in the loop. (also, this doesn't work because I happen to be on OSX, whose find
utility lacks the -printf
option...)
Dealing with space in folder name To cd into a directory with space, use quotes or backslash again. Basically, whenever you have to deal with spaces in names, you use quotes or backslash keys.
Using GNU find
and GNU sort
, one can do the following:
while IFS='' read -r -d ' ' mtime && IFS='' read -r -d '' filename; do
printf 'Processing file %q with timestamp of %s\n' "$filename" "$mtime"
done < <(find "$dir" -type f -printf '%T@ %p\0' | sort -znr)
This works as follows:
find
prints its output in the format <seconds-since-epoch> <filename><NUL>
.sort
sorts that numerically -- thus, by modification time, expressed in seconds since epoch.IFS='' read -r -d ' ' mtime
reads everything up to the space into the variable mtime
.IFS='' read -r -d '' filename
reads all remaining content up to the NUL into the variable filename
Because NUL cannot exist in filenames (as compared to newlines, which can), this can't be thrown off by names with surprising contents. See BashFAQ #3 for a detailed discussion.
Moreover, because it doesn't depend on passing names as command-line arguments to ls -t
(which, like all other external commands, can only accept a limited number of command-line arguments on each invocation), this approach is not limited in the number of files it can reliably sort. (Using find ... -exec ls -t {} +
or ... | xargs ls -t
will result in silently incorrect results when the number of filenames being processed grows larger than the number that can be passed to a single ls
invocation).
Use find
in combination with xargs
to pass file names with NUL-byte separation, and use a while
read loop for efficiency and space preservation:
find /path/to/dir -type f -print0 | xargs -0 ls -t | while read file
do
ls "$file" # or whatever you want with $file, which may have spaces
# so always enclose it in double quotes
done
find
generates the list of files, ls
arranges them, by time in this case. To reverse the sort order, replace -t
with -tr
. If you wanted to sort by size, replace -t
with -s
.
Example:
$ touch -d '2015-06-17' 'foo foo'
$ touch -d '2016-02-12' 'bar bar'
$ touch -d '2016-05-01' 'baz baz'
$ ls -1
bar bar
baz baz
foo foo
$ find . -type f -print0 | xargs -0 ls -t | while read file
> do
> ls -l "$file"
> done
-rw-rw-r-- 1 bishop bishop 0 May 1 00:00 ./baz baz
-rw-rw-r-- 1 bishop bishop 0 Feb 12 00:00 ./bar bar
-rw-rw-r-- 1 bishop bishop 0 Jun 17 2015 ./foo foo
For completeness, I'll highlight a point from comments to the question: -t
is sorting by modification time, which not strictly creation time. The file system on which these files reside dictates whether or not creation time is available. Since your initial attempts used -t
, I figured modification time was what you were concerned about, even if it's not pedantically true.
If you want creation time, you'll have to pull it from some source, like stat
or the file name if its encoded there. This basically means replacing the xargs -0 ls -t
with a suitable command piped to sort
, something like: xargs -0 stat -c '%W' | sort -n
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With