Had a directory full of files with dots in the names and needed them all cleaned up. Something like my.report.final.pdf should become my_report_final.pdf, not my_report_final..pdf or worse.
This one-liner does the job:
for f in *; do pre="${f%.*}"; suf="${f##*.}"; mv -i -- "$f" "${pre//./_}.${suf}"; done
Here’s what each part does. ${f%.*} strips everything from the last dot onwards, giving you the filename without its extension. ${f##*.} grabs just the extension by stripping everything up to and including the final dot. Then ${pre//./_} replaces every dot in the base name with an underscore.
The mv -i flag asks for confirmation before overwriting, which is useful if two files happen to map to the same name after replacement. Drop the -i if you’re confident and want it to run silently.
Run this in the directory containing your files. It won’t touch subdirectories, which is probably what you want.