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"
' 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
    mv -v "$file" "${file%/*}/$alnum_only"
' sh {} +

Other notes:

  • this solution does not take care of file extensions: file.txt will turn into file_txt.

    • if this is a problem, change ${tail//[^[:alnum:]]/_} to ${tail//[^[:alnum:].]/_} -- adding a dot to the characters to keep.
  • Have you thought about what should happen if two different files_with_special_chars both map to the same destination file?


    With my solution, only the contents of the last file will remain.

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .