I'm trying to rename files like this:

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

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...

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

  • I'm moving some files from linux, to Mac. But for some reason the Mac system cannot read all file names, so I'm trying to rename my files. Adding a space to the command above does not work. And you are correct: I want to replace all chars with '_' except the ones specified. And I don't know how to include the space char.
    – user679568
    Dec 22 '17 at 14:14
  • Please note that I'm trying to replace all chars with '_' except the ones specified. There is no particular pattern to the files being renamed, so I cannot use this perl command.
    – user679568
    Dec 22 '17 at 14:24

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


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


  • 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!

  • This command works thank you. You have also clearly identified the mistakes in the command I used.
    – user679568
    Dec 22 '17 at 14:33
  • 2
    Thank you again. Please note that there is no need to use the forward slash '\' in the case of using sed. Because this adds '\' to the list of ignored chars.
    – user679568
    Dec 22 '17 at 14:52
  • 2
    @latach That's a backslash, but you're absolutely right. After testing and digging around I found that . is literal in a character class. TIL!
    – Zanna
    Dec 22 '17 at 15:02

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._ ]/_}"

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

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy