Anchor(faq30)

How can I rename all my *.foo files to *.bar? How can I convert all upper-case file names to lower case?

Some GNU/Linux distributions have a rename command, which you can use for the former; however, the syntax differs from one distribution to the next, so it's not a portable answer. (Consult your system's man pages if you want to learn how to use yours, if you have one at all. It's often perfectly good for one-shot interactive renames, just not in portable scripts.)

You can do mass renames in POSIX shells like this:

for f in *.foo; do mv "$f" "${f%.foo}.bar"; done

This invokes the external command mv once for each file, so it may not be as efficient as some of the rename implementations.

If you want to do it recursively, then it becomes much more challenging. This example works (in ["BASH"]) as long as no files have newlines in their names:

find . -name '*.foo' -print | while IFS=$'\n' read -r f; do
  mv "$f" "${f%.foo}.bar"
done

To convert filenames to lower case:

# tolower - convert file names to lower case

for file in *
do
    [ -f "$file" ] || continue                  # ignore non-existing names
    newname=$(echo "$file" | tr 'A-Z' 'a-z')    # lower-case version of file name
    [ "$file" = "$newname" ] && continue        # nothing to do
    [ -f "$newname" ] && continue               # do not overwrite existing files
    mv "$file" "$newname"
done

Purists will insist on using

tr '[:upper:]' '[:lower:]'

in the above code, in case of non-ASCII (e.g. accented) letters in locales which have them. Note that tr can behave very strangely when using the A-Z range on some systems:

imadev:~$ echo Hello | tr A-Z a-z
hÃMMÃ

To make sure you aren't caught by surprise when using tr, either use the fancy range notations, or set your locale to C.

imadev:~$ echo Hello | LC_ALL=C tr A-Z a-z
hello
imadev:~$ echo Hello | tr '[:upper:]' '[:lower:]'
hello
# Either way is fine here.

This technique can also be used to replace all unwanted characters in a file name e.g. with '_' (underscore). The script is the same as above, only the "newname=..." line has changed.

# renamefiles - rename files whose name contain unusual characters
for file in *
do
    [ -f "$file" ] || continue                  # ignore non-existing names
    newname=$(echo "$file" | sed 's/[^a-zA-Z0-9_.]/_/g')
    [ "$file" = "$newname" ] && continue        # nothing to do
    [ -f "$newname" ] && continue               # do not overwrite existing files
    mv "$file" "$newname"
done

The character class in [] contains all allowed characters; modify it as needed.

If you have the utility "mmv" on your machine, you could simply do

mmv "*" "#l1"

Another common form of this question is "How do I rename all my MP3 files so that they have underscores instead of spaces?" You can use this:

for f in *\ *.mp3; do mv "$f" "${f// /_}"; done