Replace dots with underscores in filenames, leaving extension intact
I believe that this program will do what you want. I have tested it and it works on several interesting cases (such as no extension at all):
#!/bin/bash
for fname in *; do
name="${fname%\.*}"
extension="${fname#$name}"
newname="${name//./_}"
newfname="$newname""$extension"
if [ "$fname" != "$newfname" ]; then
echo mv "$fname" "$newfname"
#mv "$fname" "$newfname"
fi
done
The main issue you had was that the ##
expansion wasn't doing what you wanted. I've always considered shell parameter expansion in bash to be something of a black art. The explanations in the manual are not completely clear, and they lack any supporting examples of how the expansion is supposed to work. They're also rather cryptic.
Personally, I would've written a small script in sed
that fiddled the name the way I wanted, or written a small script in perl
that just did the whole thing. One of the other people who answered took that approach.
One other thing I would like to point out is my use of quoting. Every time I do anything with shell scripts I remind people to be very careful with their quoting. A huge source of problems in shell scripts is the shell interpreting things it's not supposed to. And the quoting rules are far from obvious. I believe this shell script is free of quoting issues.
Use for thisfile in *.*.*
(that is, loop over files with two dots or more in their name). Remember to quote your variables and use --
to mark the end of options as in mv -- "$thisfile" "$newname.$extension"
With zsh.
autoload -U zmv
zmv '(*).(*)' '${1//./_}.$2'
How about this:
perl -e '
@files = grep {-f} glob "*";
@old_files = @files;
map {
s!(.*)\.!$1/!;
s!\.!_!g;
s!/!.!
} @files;
rename $old_files[$_] => $files[$_] for (0..$#files)
'
DISCLAIMER: try it on a dummy directory first, I haven't tested it!