Batch rename files replacing non-alphanumeric characters with underscore
I would like to recursively rename several files located in all subdirectories of a parent folder. I would like to remove spaces and other non-alphanumeric characters and replace them with an underscore.
I have looked at several command such as tr
, for f in *
, and rename
but I'm still having struggles trying to accomplish this recursively.
How can I go about doing such a rename recursively?
When you're talking about "recursively" then find
comes into play.
cd /path/to/parent_folder
find . -depth -exec do_stuff_here +
I'm using -depth
so that files in a subdirectory get renamed before the subdirectory itself,
and -exec program {} +
so that the program receives many filenames at once, reducing startup overhead.
Since there's no builtin rename utility that ships with the mac, I'll wrote do_stuff_here
as a bash script
bash -c '
for file; do
tail=${file##*/} # part after the last slash
alnum_only=${tail//[^[:alnum:]]/_} # replace characters
mv -v "$file" "${file%/*}/$alnum_only"
done
' sh file ...
That trailing "sh" is required for bash -c '...'
one-liners -- the first argument is taken as $0
and the rest of the arguments are $1 etc
Putting it all together:
find . -depth -exec bash -c '
for file; do
tail=${file##*/}
alnum_only=${tail//[^[:alnum:]]/_}
mv -v "$file" "${file%/*}/$alnum_only"
done
' sh {} +
Other notes:
-
this solution does not take care of file extensions:
file.txt
will turn intofile_txt
.- if this is a problem, change
${tail//[^[:alnum:]]/_}
to${tail//[^[:alnum:].]/_}
-- adding a dot to the characters to keep.
- if this is a problem, change
-
Have you thought about what should happen if two different files_with_special_chars both map to the same destination file?
./subdir/file_1 ./subdir/file-1
With my solution, only the contents of the last file will remain.