How to iterate through all files in a directory, ordered by date created, with some filenames have spaces in their names How to iterate through all files in a directory, ordered by date created, with some filenames have spaces in their names unix unix

How to iterate through all files in a directory, ordered by date created, with some filenames have spaces in their names


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 filedo    ls "$file" # or whatever you want with $file, which may have spaces               # so always enclose it in double quotesdone

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 -1bar barbaz bazfoo 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


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).


You can temporarily set your IFS variable to avoid the problem with spaces (thanks to http://www.linuxjournal.com/article/10954?page=0,1)

IFS_backup=$IFSIFS=$(echo -en "\n\b")for file in `ls -t dir` ; do  #blahdoneIFS=$IFS_backup

Edit: this worked on Ubuntu, but not RHEL6. The alternative suggested by bishop appears to be more portable, for example:

ls -t dir|while read file; do ...; done