0

Our office uses dropbox on Macs to organize our design projects. For several years, many of these files and folders were being saved with characters that are illegal on Windows OS. We have some Windows OS computers we need to add to our office. This has created a problem as none of those files sync properly.

I know very little about running OSX scripts, but came across this guide that almost does what I need, except I need it to work throughout all the subfolders and files as well. The person created an automator script wrapped in a program.

Remove All Illegal Characters from All Filenames in a Given Folder

Is it possible for a script like this to include all subfolders and files as well? This script removes all spaces and special characters, but REPLACING them with an underscore would be much better if that is a possibility.

Thanks

1 Answer 1

2
  • The easy way to get slhck’s script to search subfolders and files recursively, if you're using bash, is to add shopt -s globstar and then change * to **.  This will not work correctly if you have directories with “illegal” characters in their names.  You can work around this simply by running the script n+1 times where n is the maximum number of directories with illegal characters in any path.  For example, if you have a f@cat directory, and a dog# directory below that, and a fox! directory below that, you would need to run the script four times.

    I give a better way of doing that, below.

  • slhck’s script really should say mv -i instead of mv.  If you had files named, say, cost+tax and cost-tax, the script will rename them both to costtax (or cost_tax, after we make that change).  This will clobber the first file with the second file.  The -i (interactive) option will cause mv to ask for confirmation.  You’ll have to handle such collisions like that manually.
  • To replace “illegal” characters with underscores, change ${file//[^0-9A-Za-z.]} to ${file//[^0-9A-Za-z.]/_} (or, better yet, ${file//[^0-9A-Za-z._]/_}).

So, with the above changes, slhck’s script becomes

shopt -s globstar
for f in "$1"/**
do
  dir="$(dirname "$f")"
  file="$(basename "$f")"
  mv -i -- "$f" "${dir}/${file//[^0-9A-Za-z._]/_}"
done

When running this, be absolutely sure to provide an argument (e.g., .); otherwise it will try to rename all files in the file system (starting at /).  You should probably put something in the script to verify that the $1 argument is not null.


This has problems with directories with illegal characters in their names because the ** expands to a list of all the files and directories in the sub tree under $1 with branches in top-to-bottom order.  So, if you have f@cat/dog#, it will see f@cat and f@cat/dog# as arguments.  So it will rename f@cat to f_cat, and then look for f@cat/dog# — which no longer exists, because it has been renamed to f_cat/dog#.  We can fix that by doing

find "$1" -depth -name '*[^0-9A-Za-z._]*' -exec sh -c \
          'for f do dir="$(dirname "$f")"; file="$(basename "$f")";
          mv -i -- "$f" "${dir}/${file//[^0-9A-Za-z.]/_}"; done' sh {} +

The -depth option to find tells it to look at directory branches in bottom-to-top order.  The -name directive causes it to look only at file names that need to be renamed.  (The other script will give error messages when it tries to rename files to themselves, because their names don’t have any illegal characters.)  find then passes the names to a shell that does the same thing as the script, but as a single command.

You must log in to answer this question.

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