0

I have some files and folders that are all in uppercase that I want to rename to their lowercase equivalent. What would be the best way to go about doing this in bash on a Linux system?

As an example I might have:

.
|-- FOLDER0
|   |-- SUBFOLDERA
|   `-- SUBFOLDERB
`-- FOLDER1
    `-- AFILE.TXT

And I want to convert it to:

.
|-- folder0
|   |-- subfoldera
|   `-- subfolderb
`-- folder1
    `-- afile.txt

I can probably write a depth first recursive script to do this (depth first to ensure that files and subfolders are renamed before their parent folder), but I was wondering if there is a better way. rename might be useful, but it doesn't seem to support recursion.

David Dean
  • 441
  • 6
  • 11

3 Answers3

5
find . -depth -print0 | xargs -0 rename -n '$_ = lc $_'

Take out the -n flag once you're sure that it's doing what you want.

womble
  • 95,029
  • 29
  • 173
  • 228
Rudedog
  • 732
  • 5
  • 9
  • At least on my computer, find returns the folders before the files, so won't this mean that after the folder has been renamed the filenames following it will be wrong? – David Dean Nov 26 '09 at 06:48
  • 2
    You should use the `-depth` option of `find`. – Dennis Williamson Nov 26 '09 at 06:50
  • 1
    +1 for `-print0` and its space-ridden filename handling. – Steve Schnepp Nov 26 '09 at 08:58
  • The only problem with this is that assumes that you don't have situations where there are several source filenames with the same squashed case filename, such as ThisFile.txt THISFILE.TXT and ThisFile.TXT. You'll wind up destroying all but the last in the chain. – chris Nov 26 '09 at 16:31
  • Actually, the rename command won't overwrite existing files. – Rudedog Nov 26 '09 at 17:34
  • 2
    Note that rename on some distro's (Fedora/Redhat notably) isn't the useful perl one - so this won't work. – James Nov 26 '09 at 23:35
  • James has a good point. the 'useful perl' `rename` on my Debian system is the `prename` script from the perl package: http://packages.debian.org/lenny/perl – quack quixote Nov 27 '09 at 03:25
3

This is more of a comment and a meta observation on the way this forum works, but I feel pretty strongly that it should be stated more loudly than if I just left a comment in rudedog's answer. This is also a comment directed more at people who may have a problem similar to David Dean's question, but not identical.

It is easy to do a huge amount of damage with recursive operations that rename files. Let me say that again because it is very important. You can destroy a huge amount of data very very very quickly with recursive operations that rename files. You can quickly render a system totally completely unusable. You can delete 99% of your data very quickly. Such actions should be done very carefully and with a lot of thought, backups, and test runs.

I am very wary of an answer to this specific question that relies on several specific implied assumptions:

  1. The version of "rename" that I'm using is the same version you're using and that they both have the safety features that I'm accustomed to. "rename" is not some standard, normal utility in unix, as far as I know. It seems to be some clever script that exists in some distribution.
  2. "rename" as described has some safety feature that prevents overwriting a file.
  3. either the files being munged have unambiguous squashed-case results or it isn't a big deal if the resulting subdirectory has some upper-case files because the squash-case results had a collision and the name-changing mechanism does nothing if the target name already exists.

I'm perfectly comfortable doing things that are shortcuts. I try to understand, however, that something is a shortcut and what the limitations of such a shortcut are, so that I know when I need to run the always-correct program or the 10,000 times faster but fails in some edge cases shortcut.

In short -- the above script using rename is 100% reasonable in 90% of cases. The 100% correct in 100% of cases solution is actually a little bit complex and requires some smarts in dealing with namespace collisions.

But, as I said before, be very careful with recursive file operations, especially if they only munge metadata such as permissions or filenames. They're fast and difficult to reverse without going to backups...

Here's an example of how to write the "rename" command in bash or ksh or some other modernish shell that has "typeset". Let's call this script /tmp/squash

#!/usr/local/bin/pdksh
# use some shell that supports typeset such as ksh, pdksh, or bash
# call this script from "xargs -0" and feed it with "find -print0"

typeset -l targetname || { echo "This shell doesn't support typeset!" >&2 ; exit 1 ; }
for file
do
  count=""
  target="$file"
  targetpath="${target%/*}/"
  targetname="${target##*/}"
  if
    [[ "$file" == "$targetpath$targetname" ]]  || [[ "$file" == . ]]
  then
    :
  else
    while
      test -e "$targetpath/$targetname""$count"
    do
      count=$((${count:-0}+1))
    done
    echo "renaming $file to $targetpath$targetname$count" >&2
    mv -n  "$file" "$targetpath$targetname$count"
  fi
done

Now, you can run the following two commands and have your files all squashed and not have any file overwritten.

find /path/to/files -type f -print0 | xargs -0 /tmp/squash
find /path/to/files -type d -depth -print0 | xargs -0 /tmp/squash

And I'm making the assumptions that the directories in question won't have stuff put into them or stuff renamed while I'm running the program.

chris
  • 11,784
  • 6
  • 41
  • 51
0

I'll defer to others for the answer, but -type f and -type d as arguments for find will return files and directories, respectively. If you wanted to change files first, then directories... then change files first and then directories.