How to iterate over all files in a directory sorted by creation date, with some file names that have spaces in their names

I have first

for file in `ls -t dir` ; do #blah done 

but files with spaces are divided into two iterations.

I found many variations on this that fix the problem with spaces, but then leave the date information in the $file variable.

Edit: show one such variation:

 for file in `find . -printf "% T@ %Tc %p\n" | sort -n` ; do #blah done 

The problem is that all the time information is still in place in the $file variable in the loop. (this also does not work, because I ended up on OSX, whose find utility does not have the -printf option ...)

+9
source share
3 answers

Use find in conjunction with xargs to pass file names separated by NUL bytes and use a while read while to ensure efficiency and save space:

 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 a list of files, ls arranges them by time in this case. To reverse the sort order, replace -t with -tr . If you want 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 will highlight a point with comments on the question: -t sorts by modification time, which is not a strict creation time. The file system on which these files are located dictates whether the creation time is available. Since your initial attempts were made using -t , I figured the modification time was what you were worried about, even if that wasn't pedantic.

If you want to create a time, you will have to pull it from some source, for example, stat or the file name if it is encoded there. This basically means replacing xargs -0 ls -t with a suitable command with sort number, for example: xargs -0 stat -c '%W' | sort -n xargs -0 stat -c '%W' | sort -n

+4
source

Using GNU find and GNU sort , you 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 displays its output in the format <seconds-since-epoch> <filename><NUL> .
  • sort sorts that numerically - thus, by modification time, expressed in seconds from the era.
  • IFS='' read -r -d ' ' mtime reads everything to the place in the mtime variable.
  • IFS='' read -r -d '' filename reads all remaining contents up to NUL into the filename variable

Since NUL cannot exist in file names (compared to newlines that can), this cannot be overridden by names with amazing content. See BashFAQ # 3 for more details.

In addition, since it does not 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 for each call), this approach is not limited to the number of files that It can reliably sort. (Using find ... -exec ls -t {} + or ... | xargs ls -t will produce silent results when the number of processed file names is greater than the number that can be passed to a single ls call).

0
source

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

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

Edit: this worked on Ubuntu, but not on RHEL6. The alternative proposed by the bishop seems more portable, for example:

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

All Articles