Remove special characters and replace space with _ from files and directories Recursively in Bash
Untested

find . -depth -print0 |
while IFS= read -d '' -r file; do
    dir=$(dirname "$file")
    base=$(basename "$file") 
    base=${base//[[:space:]]/_}         # change whitespace to underscores
    base=${base//[^[:alnum:]_.]/}       # delete any chars not alphanumeric _ .
    newname="$dir/$base"
    if [[ -e $newname ]]; then
        echo "'$newname' exists, not renaming '$file'"
    else
        echo mv "$file" "$newname"
    fi
done


If it looks OK, remove the "echo"

The key here is the "-depth" option to find. It will emit "./sub dir/file name" before "./sub dir".  Otherwise if "./sub dir" is renamed to "./subdir", then you'll get a 'file not found' error when the while loops gets to "./sub dir/file name". Also, it means you don't need different logic for directories.

The "-print0" option to find and the busy-ness around the read command are to ensure filenames with spaces are handled properly.