How to process files with spaces using bash
The other day I wanted to rename a whole bunch of images with spaces and brackets in them. I had files of the form filename1 (1).JPG, filename1 (2).JPG, ..., filename2 (1).JPG, ...
In case you are wondering, these file came from a windows box where copies of files get saved in the format filename
for i in `ls -1 *.JPG`; do j=`echo $i | tr -d "[:blank:]" | tr "[:upper:]" "[:lower:]" | tr "(" "_" | tr -d ")"`; mv "$i" "$j"; done
The idea is to process each file name in a loop, transform the filename with a a set of tr calls and then do a final rename of the files using mv. tr takes its input from standard in and writes to standard out, hence the trick of first echoing the current value of the iteration, passing it through several tr calls and then assigning it to a temporary variable. Note the use of the character classes for the transformation via tr.
This one liner works fine for file names without spaces, but as soon as there are spaces in the file names you are getting into trouble. The default field separator of a for loop is the space character which means file names with spaces are causing two iterations of the loop. One solution to the problem is to set the $IFS variable prior to executing the for loop. The value of this variable determines the field separator. However, in this case you also have to remember to reset the variable afterward.
Another approach is to use a completely different construct which you don't see so often. Let's use find together with a while loop.
find . -name "*.JPG" -print0 | while read -d $'\0' file; do j=`echo $file | tr -d "[:blank:]" | tr "[:upper:]" "[:lower:]" | tr "(" "_" | tr -d ")"`; mv "$file" $j; done
We use find together with the -print0 option to print all file names into one line separated by a ASCII NUL character. In the loop we use read together with the -d option which determines the delimiter for read. The rest is the same as in the for loop solution.
Hope this helps some people, but maybe its just me who gets frustrated with spaces in file names ;-)