Shell script to fix bad filenames?
I'd use bash and find. I'm sure there's a simpler option but here's what I came up with:
-
This can deal with file names containing "/" (find will give a warning, ignore it), but it will only work on files in the current directory (no subdirectories). I couldn't figure out how to tell bash or find to differentiate between a "/" in a file name and a "/" that is part of the path.
for i in $(find . -maxdepth 1 -type f -name "*[\:\;><\@\$\#\&\(\)\?\\\/\%]*" | sed 's/\.\///'); do mv "$i" ${i//[\;><\@\$\#\&\(\)\?\\\/\%]/_}; done
-
This one cannot deal with file names containing "/" but it will work on all files in the current directory and its subdirectories:
for i in $(find . -type f -name "*[\:\;\>\<\@\$\#\&\(\)\?\\\%]*"); do mv "$i" ${i//[\;><\@\$\#\&\(\)\?\\\%]/_}; done
Make sure to test these before running. They worked fine in the few tests I ran, but I was not exhaustive. Also bear in mind that I am on a linux system. The particular implementation of find, and perhaps bash, may differ on yours.
EDIT: Changing the mv $i
command to `mv -i $i‘ will cause mv to prompt you before overwriting an existing file.
EDIT2: To deal with filenames with spaces, you can change the bash IFS (Input Field Separator) variable like so (adapted from here):
SAVEIFS=$IFS; IFS=$(echo -en "\n\b"); for i in $(find . -type f -name "*[\:\;\>\<\@\$\#\&\(\)\?\\\%\ ]*"); do mv "$i" ${i//[\;><\@\$\#\&\(\)\?\\\%\ ]/_}; done; IFS=$SAVEIFS
I also modified the regular expression to match/replace spaces with underscores. The SAVEIFS bit just returns the IFS variable to its original configuration.
EXPLANATION:
for i in $(command); do something $i; done
This is a generic bash loop. It will go through a command's output, sequentially setting variable $i to each of the values returned by command, and will do something to it.
find . -maxdepth 1 -type f -name "*[\:\;><\@\$\#\&\(\)\?\\\/\%]*" '
Find all files in the current directory whose name contains one of the following characters: :;><@$#&()\/%
. To add more, just escape them with "\" (eg "\¿") and add them to the list within the brackets ([ ]). Probably, not all these characters need to be escaped, but I can never remember which are special variables in which environment so I escape everything, just in case.
sed 's/\.\///
Remove the current directory from find's output, print "foo" instead of "./foo".
mv "$i" ${i//[\;><\@\$\#\&\(\)\?\\\/\%]/_}
Every time this little scipt loops, $i will be the name of a badly named file. This command will move (rename) that file changing all unwanted characters to "_". Look up bash substitution for more information.