Avoid replacing spaces when renaming files

I'm trying to rename files like this:

for file in *;
do
mv -i "$file" "$(echo "$file" | sed -e 's/[^A-Za-z0-9._-]/_/g')";
done

But the sed command replaces all spaces with _.

How can I edit the sed command to make it inlcude spaces along with the specified characters? I've tried using \s but it does not work...

EDIT:
For example: the file trip: hill, should be renamed: trip_ hill, but the command above makes it trip__hill.


Do not parse filenames with sed! The output of echo "$file" may not be reliable.

Use rename. On 17.10 you need to install it first

sudo apt install rename

Then:

rename -n -- 's/[^-A-Za-z0-9_ .]/_/g' *

Notes

  • remove -n after testing to actually rename the files
  • -- end-of-options in case any file begins with -
  • [^-A-Za-z0-9_ .] characters we do not want to replace - put - first or last so it can't indicate a range (it is treated literally in these positions).
  • Spaces can be included in the class
  • . is treated literally (in other regex contexts it stands for any character and needs to be escaped).

This also works in sed:

$ echo 'trip: hill' | sed 's/[^-A-Za-z0-9 _.]/_/g'
trip_ hill

If I add a space to the end in your version, I get an error:

$ echo 'trip: hill' | sed -e 's/[^A-Za-z0-9._- ]/_/g'
sed: -e expression #1, char 22: Invalid range end

But with - at the end, it works:

$ echo 'trip: hill' | sed -e 's/[^A-Za-z0-9._ -]/_/g'
trip_ hill

So perhaps the position of the hyphen caused your problem when you added the space. But the advice not to parse filenames stands!


You can also just use the shell, Bash's parameter expansion can do substitution:

for f in ./* ; do
    mv "$f" "${f//[^-A-Za-z0-9._ ]/_}"
done

The double slash tells it to replace all matches, other than that, the syntax is straightforward.