2

How can I replace all underscore chars with a whitespace in multiple file names using Bash Script? Using this code we can replace underscore with dash. But how it works with whitespace?

for i in *.mp3;
do x=$(echo $i | grep '_' | sed 's/_/\-/g');
if [ -n "$x" ];
then mv $i $x;
fi;
done;

Thank you!

stardust
  • 343
  • 3
  • 17

3 Answers3

9

This should do:

for i in *.mp3; do
    [[ "$i" = *_* ]] && mv -nv -- "$i" "${i//_/ }"
done
  • The test [[ "$i" = *_* ]] tests if file name contains any underscore and if it does, will mv the file, where "${i//_/ }" expands to i where all the underscores have been replaced with a space (see shell parameter expansions).
  • The option -n to mv means no clobber: will not overwrite any existent file (quite safe). Optional.
  • The option -v to mv is for verbose: will say what it's doing (if you want to see what's happening). Very optional.
  • The -- is here to tell mv that the arguments will start right here. This is always good practice, as if a file name starts with a -, mv will try to interpret it as an option, and your script will fail. Very good practice.

Another comment: When using globs (i.e., for i in *.mp3), it's always very good to either set shopt -s nullglob or shopt -s failglob. The former will make *.mp3 expand to nothing if no files match the pattern (so the loop will not be executed), the latter will explicitly raise an error. Without these options, if no files matching *.mp3 are present, the code inside loop will be executed with i having the verbatim value *.mp3 which can cause problems. (well, there won't be any problems here because of the guard [[ "$i" = *_* ]], but it's a good habit to always use either option).

Hope this helps!

gniourf_gniourf
  • 44,650
  • 9
  • 93
  • 104
  • ... could be better written `[[ "$i" == "*_*" ]]` as this make less confusion while `==` is used for test and `=` for assign (at all I didn't found syntax `[[ LHS = RHS ]]` in manpage). – F. Hauri - Give Up GitHub Dec 22 '12 at 08:36
  • @F.Hauri Scroll down near the bottom of section _Bash Conditional Expressions_ of the [bash reference manual](http://www.gnu.org/software/bash/manual/bashref.html#Bash-Conditional-Expressions) for `=` used with `[[`. Otherwise, you shouldn't quote the `*_*` in the right-hand side. So what I wrote is correct. – gniourf_gniourf Dec 22 '12 at 09:23
  • Thanks, I found it!. Two syntax seem to be alias for *POSIX* conformance. At all, new syntax is less confuse. – F. Hauri - Give Up GitHub Dec 22 '12 at 09:49
3

The reason your script is failing with spaces is that the filename gets treated as multiple arguments when passed to mv. You'll need to quote the filenames so that each filename is treated as a single agrument. Update the relevant line in your script with:

mv "$i" "$x"  
# where $i is your original filename, and $x is the new name

As an aside, if you have the perl version of the rename command installed, you skip the script and achieve the same thing using:

rename 's/_/ /' *.mp3

Or if you have the more classic rename command:

rename "_" " " *.mp3
Shawn Chin
  • 84,080
  • 19
  • 162
  • 191
1

Using tr

tr '_' ' ' <file1 >file2
cmh
  • 10,612
  • 5
  • 30
  • 40