Differences between revisions 1 and 2
Revision 1 as of 2007-05-02 23:18:24
Size: 3079
Editor: redondos
Comment:
Revision 2 as of 2007-05-03 13:50:05
Size: 3678
Editor: GreyCat
Comment: massive clean-up
Deletions are marked like this. Additions are marked like this.
Line 2: Line 2:
== How can I rename all my *.foo files to *.bar?  How can I convert all upper-case file names to lower case? == == How can I rename all my *.foo files to *.bar, or convert spaces to underscores, or convert upper-case file names to lower case? ==
Line 4: Line 4:
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.) Some GNU/Linux distributions have a {{{rename(1)}}} 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. We don't include any {{{rename(1)}}} examples here because it's too confusing -- there are two common versions of it and they're totally incompatible with each other.
Line 6: Line 6:
You can do mass renames in POSIX shells like this: You can do mass renames in POSIX shells with [#faq73 Parameter Expansion], like this:
Line 12: Line 12:
Here's a similar example, this time replacing spaces in filenames with underscores:

{{{
for f in *\ *; do mv "$f" "${f// /_}"; done
}}}
Line 14: Line 20:
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: If you want to do it recursively, then it becomes much more challenging. This example for renaming {{{*.foo}}} to {{{*.bar}}} works (in ["BASH"]) as long as no files have newlines in their names:
Line 21: Line 27:

For more techniques on dealing with files with inconvenient characters in their names, see [#faq20 FAQ #20].
Line 29: Line 37:
    [ -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
    [ -f "$file" ] || continue # ignore non-existing names
    newname=$(echo "$file" | tr '[:upper:]' '[:lower:]') # lower case
    [ "$file" = "$newname" ] && continue # nothing to do
    [ -f "$newname" ] && continue # don't overwrite existing files
Line 37: Line 45:
Purists will insist on using
{{{
tr '[:upp
er:]' '[: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:
We use the fancy range notation, because {{{tr}}} can behave ''very'' strangely when using the {{{A-Z}}} range on some systems:
Line 45: Line 49:
hÃMMÃ hÉMMÓ
Line 48: Line 52:
To make sure you aren't caught by surprise when using {{{tr}}}, either use the fancy range notations, or set your locale to C. To make sure you aren't caught by surprise when using {{{tr}}} with ranges, either use the fancy range notations, or set your locale to C.
Line 58: Line 62:
Or, if you have the utility {{{mmv(1)}}} on your machine, you could simply do:

{{{
# convert all filenames to lowercase
mmv "*" "#l1"
}}}
Line 62: Line 73:
# Portable version.
Line 64: Line 76:
    [ -f "$file" ] || continue       # ignore non-existing names     [ -f "$file" ] || continue # ignore non-regular files, etc.
Line 66: Line 78:
    [ "$file" = "$newname" ] && continue       # nothing to do
    [ -f "$newname" ] && continue       # do not overwrite existing files
    [ "$file" = "$newname" ] && continue # nothing to do
    [ -f "$newname" ] && continue # do not overwrite existing files
Line 74: Line 86:
If you have the utility "mmv" on your machine, you could simply do Here's an example that does the same thing, but this time using Parameter Expansion instead:
Line 77: Line 89:
mmv "*" "#l1" # renamefiles (more efficient, less portable version)
for file in *; do
   [ -f "$file" ] || continue
   newname=${f//[^a-zA-Z0-9_.]/_}
   [ "$file" = "$newname" ] && continue
   [ -f "$newname" ] && continue
   mv "$file" "$newname"
done
Line 79: Line 98:

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

Anchor(faq30)

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

Some GNU/Linux distributions have a rename(1) 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. We don't include any rename(1) examples here because it's too confusing -- there are two common versions of it and they're totally incompatible with each other.

You can do mass renames in POSIX shells with [#faq73 Parameter Expansion], like this:

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

Here's a similar example, this time replacing spaces in filenames with underscores:

for f in *\ *; do mv "$f" "${f// /_}"; 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 for renaming *.foo to *.bar 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

For more techniques on dealing with files with inconvenient characters in their names, see [#faq20 FAQ #20].

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 '[:upper:]' '[:lower:]')     # lower case
    [ "$file" = "$newname" ] && continue      # nothing to do
    [ -f "$newname" ] && continue             # don't overwrite existing files
    mv "$file" "$newname"
done

We use the fancy range notation, because 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 with ranges, 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.

Or, if you have the utility mmv(1) on your machine, you could simply do:

# convert all filenames to lowercase
mmv "*" "#l1"

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
# Portable version.
for file in *
do
    [ -f "$file" ] || continue            # ignore non-regular files, etc.
    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.

Here's an example that does the same thing, but this time using Parameter Expansion instead:

# renamefiles (more efficient, less portable version)
for file in *; do
   [ -f "$file" ] || continue
   newname=${f//[^a-zA-Z0-9_.]/_}
   [ "$file" = "$newname" ] && continue
   [ -f "$newname" ] && continue
   mv "$file" "$newname"
done

BashFAQ/030 (last edited 2020-06-10 14:10:22 by GreyCat)