How do I rename "*.foo" to "*.bar", or change file names to lowercase?


  

O.k., you have a a bunch of files that have "*.foo" file extention, and you need to rename all of them to "*.boo." Thus you try "mv *.foo *.bar" at command prompt. However, as you already know by now, that would not work.

Why doesn't "mv *.foo *.bar" work? Think about how the shell expands wildcards. "*.foo" and "*.bar" are expanded before the mv command ever sees the arguments. Depending on your shell, this can fail in a couple of ways. Csh (=C shell) prints "No match." because it can't match "*.bar". Sh (=Bourne Shell) executes "mv a.foo b.foo c.foo *.bar", which will only succeed if you happen to have a single directory named "*.bar", which is very unlikely and almost certainly not what you had in mind.

Depending on your shell, you can do it with a loop to "mv" each file individually. If your system has "basename", you can use:

C Shell:

foreach f ( *.foo )
       set base=`basename $f .foo`
       mv $f $base.bar
end

Bourne Shell:

for f in *.foo; do
       base=`basename $f .foo`
       mv $f $base.bar
done

Some shells have their own variable substitution features, so instead of using "basename", you can use simpler loops like:

C Shell:

foreach f ( *.foo )
       mv $f $f:r.bar
end

Korn Shell:

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

If you don't have "basename" or want to do something like renaming foo.* to bar.*, you can use something like "sed" to strip apart the original file name in other ways, but the general looping idea is the same. You can also convert file names into "mv" commands with 'sed', and hand the commands off to "sh" for execution. Try

$ ls -d *.foo | sed -e 's/.*/mv & &/' -e 's/foo$/bar/' | sh

Shell loops like the above can also be used to translate file names from upper to lower case or vice versa. You could use something like this to rename uppercase files to lowercase:

C Shell:

foreach f ( * )
       mv $f `echo $f | tr '[A-Z]' '[a-z]'`
end

Bourne Shell:

for f in *; do
       mv $f `echo $f | tr '[A-Z]' '[a-z]'`
done

Korn Shell:

typeset -l l
for f in *; do
       l="$f"
       mv $f $l
done

If you wanted to be really thorough and handle files with `funny' names (embedded blanks or whatever) you'd need to use

Bourne Shell:

for f in *; do
       g=`expr "xxx$f" : 'xxx\(.*\)' | tr '[A-Z]' '[a-z]'`
       mv "$f" "$g"
done

The `expr' command will always print the filename, even if it equals `-n' or if it contains a System V escape sequence like `\c'.

Some versions of "tr" require the [ and ], some don't. It happens to be harmless to include them in this particular example; versions of tr that don't want the [] will conveniently think they are supposed to translate '[' to '[' and ']' to ']'.