ARCHIVED: In Unix, how do I rename *.foo to *.bar, or change filenames to lowercase?

This content has been archived, and is no longer maintained by Indiana University. Information here may no longer be accurate, and links may no longer be available or reliable.

In Unix, if you are attempting to change the extension for multiple files, you may be tempted to try the command mv *.foo *.bar thinking that this will move each file ending in .foo to a new file ending in .bar. When considering why this command does not work, think about how the shell expands wildcards. Before the mv command ever sees the arguments, *.foo and *.bar are expanded. Depending on your shell, this can fail in a couple of ways. The csh shell prints "No match", because it can't match *.bar. The sh 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 accomplish this 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 or Korn 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 similar to renaming foo.* to bar.*, you can use a command such as sed to strip apart the original filename in other ways, but the general looping idea is the same. You can also convert filenames into mv commands with sed, and hand the commands off to sh for execution. Try the following:

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

A program by Vladimir Lanin called mmv that does this job nicely was posted to comp.sources.unix (Volume 21, issues 87 and 88) in April 1990. It lets you use the following:

  mmv '*.foo' '=1.bar'

You can use shell loops like the above to translate filenames from upper- to lowercase or vice versa. 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 wish to be extremely thorough and handle files with unusual names (e.g., embedded blanks) 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 (e.g., \c).

If you have the Perl language installed, you may find this rename script by Larry Wall very useful; you can use it to accomplish a wide variety of filename changes:

  #!/usr/bin/perl
  #
  # rename script examples from lwall:
  #       rename 's/\.orig$//' *.orig
  #       rename 'y/A-Z/a-z/ unless /^Make/' *
  #       rename '$_ .= ".bad"' *.f
  #       rename 'print "$_: "; s/foo/bar/ if <stdin> =~ /^y/i' *

  $op = shift;
  for (@ARGV) {
      $was = $_;
      eval $op;
      die $@ if $@;
      rename($was,$_) unless $was eq $_;
  }

Note:
This information comes from the Unix FAQ.

At Indiana University, for personal or departmental Linux or Unix systems support, see Get help for Linux or Unix at IU.

This is document abec in the Knowledge Base.
Last modified on 2018-01-18 09:23:10.