Convert Filenames to Lowercase

July 25th, 2008 by Phil Hughes in

Your rating: None Average: 4.6 (7 votes)
I was going to claim I wrote this yesterday but the timestamp on the file is 22 Nov 1998. Nothing new, nothing fancy but it does the job.

Back in the good old days, there was an operating system that didn't seem to think NAME and name were different. The result was that sometimes when you transfered files from a floppy disk (remember them?) created on that Dumb Old System, you would clutter your directory with uppercase filenames. As us UNIX old-timers learned a nifty trick to get directory names to sort before filenames in the output of the ls command (namely, start directory names with an uppercase letter), having filenames with uppercase letters was irritating.

After using the mv command all too many times and typing things like mv FILE.TXT file.txt, I wrote this script. I was thinking I could put a new coat of paint on it but, in reality, it does the job and is easy to understand. (The line numbers are there, of course, just for reference.)

    1   #!/bin/sh
    2   # lowerit
    3   # convert all file names in the current directory to lower case
    4   # only operates on plain files--does not change the name of directories
    5   # will ask for verification before overwriting an existing file
    6   for x in `ls`
    7     do
    8     if [ ! -f $x ]; then
    9       continue
   10       fi
   11     lc=`echo $x  | tr '[A-Z]' '[a-z]'`
   12     if [ $lc != $x ]; then
   13       mv -i $x $lc
   14     fi
   15     done

Line 6 starts a loop (which ends with line 15). The ls command returns a list of filenames which are sequentially assigned to the shell variable x. The if test (lines 8 through 10) checks to see if the current filename is that of a plain file. If not, the remainder of the statements in the current loop iteration are skipped.

If line 11 is to be executed we know that we have an ordinary file. Using tr we convert the filename to lowercase and assign the new name to the shell variable lc. Line 12 then checks to see if the lowercase version of the name differs from the original. If it does, line 13 is executed to change the name of the original file to the new lowercase name. The -i option causes the mv to prompt for confirmation if executing the command would overwrite an existing filename. __________________________

Phil Hughes


Special Magazine Offer -- Free Gift with Subscription
Receive a free digital copy of Linux Journal's System Administration Special Edition as well as instant online access to current and past issues. CLICK HERE for offer

Linux Journal: delivering readers the advice and inspiration they need to get the most out of their Linux systems since 1994.

Comment viewing options

Select your preferred way to display the comments and click "Save settings" to activate your changes.
.inc's picture

zshell

On March 8th, 2009 .inc (not verified) says:

Hello there, great job but i use other way:

It includes using Zshell [ zsh ] an is as simple as:

zmv '(*)' '${(L)1}'

Have a good day.

Paulo Sedrez's picture

Now we have convmv

On July 30th, 2008 Paulo Sedrez (not verified) says:

I have made my own scripts (tolower, toupper) back in the early 1990's, but now we have convmv:

convmv --lower --notest *

(note the --notest - the default is to show the actions instead of doing it)

It can also convert character sets, including - but not limited to - UTF-8 normalization forms C (Linux and many others) and D (Mac OS X).

David Tangye's picture

Better lowercase mousetrap

On July 30th, 2008 David Tangye (not verified) says:

For handling names with spaces, and not being limited to all of the current directory, use parameters, eg * or *.jpg or whatever, try:

#!/bin/bash

while [ "$1" ] ; do
f="$1"
shift
echo -ne "\n$f:"
if [ -f "$f" ]; then
echo -n "(file) "
lc=`echo $f | awk '{print(tolower($0));}'`
if [ "$lc" != "$f" ]; then
echo "-> '$lc'"
mv -i "$f" "$lc"
fi
fi
done

Anonymous's picture

Have a C program to do something similar

On July 29th, 2008 Anonymous (not verified) says:

Hey,

not much of a script writer myself, but I have something like this in a suit of shell utilities I wrote
hosted at:
ioe.sf.net

also includes nifty tools for command line image processing - useful in conjunction with netpbm.

unfortunately, I don't use a lot of standard libraries and reimplemented things myself for learning purposes, but its all good.

There is a lot of good information in the comments. Note that if I were to write the script today I would do it different. At the time (I don't know when I really wrote it--the date was a filesystem timestamp on an LS-120 disk I was reading so I am pretty sure it was written years before that--it was to convert MS-DOS file names to lower case. Thus, some of the concerns (such as a space in a filename) were just not considerations.

__________________________

Phil Hughes

Anonymous Beast's picture

Next line works only for

On July 28th, 2008 Anonymous Beast (not verified) says:

Next line works only for english
lc=`echo $x | tr '[A-Z]' '[a-z]'`

Please use next line for real lowercase:
lc=`echo $x | awk '{print(tolower($0));}'`

techno's picture

another way

On July 27th, 2008 techno (not verified) says:

for i in *
do
mv -f "$i" `echo "$i" | tr A-Z a-z`
done

Paul C's picture

Slight improvement for extended chars...

On July 27th, 2008 Paul C (not verified) says:

Not sure if this will work for unicode but it should at least handle latin charset beyond the a-z range - change:

lc=`echo $x | tr '[A-Z]' '[a-z]'`

to

lc=`echo $x | tr "[:upper:]" "[:lower:]"`

Simon Stroh's picture

Why not simply use rename ?

On July 27th, 2008 Simon Stroh (not verified) says:

Hi there ! =)
This can also be done easier with the "rename" tool that ships with perl:

rename 's/.*/-f&&lc||$_/e' *

well, shorter at least :-)

regards, Simon

brother.sand's picture

lower case, use rename

On July 27th, 2008 brother.sand says:

Alternatively, if your distro happens to include the "rename" command you can simply do this:

~$ rename 'y/A-Z/a-z/' *

That sets all files in the current directory to lower case. 'natch.

D.
--

Anonymous's picture

rename

On July 27th, 2008 Anonymous (not verified) says:

If available on your system "rename" is also a very nice command. It is able to handle regular expressions. For example to lowercase all files ending with ".txt" you could use this one command:

rename 'y/A-Z/a-z/' *.txt

You can find more information on this page:
renaming multiple files

Anonymous's picture

Bugs in this script

On July 27th, 2008 Anonymous (not verified) says:

This script will not perform correctly, and can even be dangerous to use, because it doesn't properly handle special characters in filenames. What if the filenames contain single quotes, or double quotes, or spaces, or asterisks, or combinations of such? An extreme case is when a file is called "* a"...

dm's picture

a safer version

On July 26th, 2008 dm says:

nice script, however lines 8 and 12 are unsafe (will break for file names with embedded spaces... so it's kinda a bug ;-)

double square brackets test will always work:

[[ -f $fileName ]] && ...

if you prefer to stick to old 1998 test, wrap the test parameters in double quotation marks.

Brent's picture

I used to have a little

On July 26th, 2008 Brent (not verified) says:

I used to have a little alias for the job using xargs, but now I just use:


rename 'y/A-Z/a-z/' *

Anonymous's picture

Hi, Brent! That's cool!

On July 27th, 2008 Anonymous (not verified) says:

Hi, Brent! That's cool!

Marin's picture

It's called Perl rename

On July 27th, 2008 Marin (not verified) says:

since it uses Perl regex to do the whole thing. Also, you can use this to rename files with some pattern -- that's why regex was created and I love it!

Not sure if you're just focused on the bash way to do this. I wrote something like this a while back in Python that I've found pretty useful, especially for music collections. It does some other name normalization, too.

__________________________

Micah Elliott | http://MicahElliott.blogspot.com

Anonymous's picture

Use of `ls`

On July 25th, 2008 Anonymous (not verified) says:

What advantage is there in using:

for x in `ls`

over

for x in *
?

Anonymous's picture

There isn't an advantage, in

On July 27th, 2008 Anonymous (not verified) says:

There isn't an advantage, in fact it's more of a disadvantage due to bash's word splitting. It won't work if your filenames have spaces. See the following example:

$ touch "file-without-spaces" "file with spaces"
$ for i in *; do echo "--- $i ---"; done
--- file-without-spaces ---
--- file with spaces ---
$ for i in `ls`; do echo "--- $i ---"; done
--- file-without-spaces ---
--- file ---
--- with ---
--- spaces ---

Anonymous's picture

I always change the IFS var

On July 27th, 2008 Anonymous (not verified) says:

I always change the IFS var when dealing with files with spaces

IFS=$'\n'

for FILES in $(find /some/dir)
do
echo $FILES
done

Dabe's picture

while read i

On July 27th, 2008 Dabe (not verified) says:

Personally I've grown accustomed to:

  /bin/ls | while read i; do
     blah blah "$i" | foo >> bar.out
  done

My "smv" (sed + mv) bash function looks like:

function smv () {

   if [ "$#" -lt 2 ]; then
        echo "Usage: smv 's/pat/tern/'  [ ...]" 1>&2
        return
   else
        local pattern
        pattern="$1"; shift

        while [ "$#" -ge 1 ]; do
            new=`echo "$1" | sed -e "$pattern"`
            echo mv \"$1\" \"$new\"
            mv "$1" "$new"
            shift
        done

   fi
}
Anonymous's picture

None, however he's using sh,

On July 27th, 2008 Anonymous (not verified) says:

None, however he's using sh, not bash.

"for x in *" is is far better, as would using [['s be better, since he's not even quoting his variables.

Best still, he could just be using the tool rename, most distros have it available for installing.

Reid's picture

My "tolower" Perl script

On July 28th, 2008 Reid (not verified) says:

#!/usr/bin/perl -s

if(@ARGV == 0) {
	opendir PWD, ".";
	@ARGV = readdir PWD;
	closedir PWD;
}

foreach (@ARGV) {
	# skip if no upper case!
	next unless /[[:upper:]]/;
	warn "# starting \"$_\"\n" if $v; # verbose mode
	next if $_ eq ".";
	next if $_ eq "..";
	next if m#.*/\.\.?$#;
	$pathname = $_;
	warn "# Doing \"$pathname\"\n" if $v; # verbose mode
	if($r && -d $_ && /^[^\/]+$/) {
		print "# visiting directory $_\n";
		opendir DIR, $_;
		my @dir = readdir DIR;
		closedir DIR;
		foreach my $entry (@dir) {
			next if $entry eq "..";
			next if $entry eq ".";
			warn "# Adding $_/$entry\n" if $v; # verbose mode
			unshift @ARGV, "$_/$entry";
		}
		warn "# Redo-ing\n" if $v; # verbose mode
		redo;
	}
	if(($dir, $filename) = m!(.*/)(.*)!) {
		$_ = $filename;
	} else {
		$dir ="";
	}
	# unicode-friendly way to do equivalent of English [A-Z] -> [a-z]
	s/[([:upper:])]/\L$1\E/g;
	print "mv $pathname $dir$_\n";
	$xx="xx01";
	unless($n) {
		if(-e "${dir}${xx}.$$") {
			# leave it there, increment xx
			$xx++;
			if(-e "${dir}${xx}.$$") {
				die "\"${dir}${xx}.$$\" no longer safe. Quitting.\n";
			}
		}
		print "# moving \"$pathname\" to \"${dir}${xx}.$$\"\n" if $v; # verbose mode
		# rename twice in case of case-insensitive file systems
		rename($pathname, "${dir}${xx}.$$")
		|| print "# couldn't rename \"$pathname\" to \"${dir}${xx}.$$\": $!\n";
		print "# moving \"${dir}${xx}.$$\" to \"$dir$_\"\n" if $v; # verbose mode
		rename("${dir}${xx}.$$", "$dir$_")
		|| print "# couldn't rename \"${dir}${xx}.$$\" to \"$dir$_\": $!\n";
	}
}

exit(0);
PaulTTT's picture

Many ways to do this:

On July 29th, 2008 PaulTTT (not verified) says:

This:

rename 'y/A-Z/a-z/' *

is clearly the tersest, but there are other ways.

Post new comment

Please note that comments may not appear immediately, so there is no need to repost your comment.
The content of this field is kept private and will not be shown publicly.
  • Allowed HTML tags: <a> <em> <strong> <cite> <code> <pre> <ul> <ol> <li> <dl> <dt> <dd> <i> <b>
  • Lines and paragraphs break automatically.

More information about formatting options

Newsletter

Each week Linux Journal editors will tell you what's hot in the world of Linux. You will receive late breaking news, technical tips and tricks, and links to in-depth stories featured on www.linuxjournal.com.
Sign up for our Email Newsletter

Tech Tip Videos

From the Magazine

July 2009, #183

News Flash: Linux Kernel 3.0 to include an on-the-go Expresso machine interface! Ok, maybe not, but Linux is definitely going mobile, from phones to e-readers. Find out more inside about Android, the Kindle 2, the Western Digital MyBook II, The Bug, and Indamixx (a portable recording studio). And if you've gone mobile and you been wanting more Emacs in your life then check out Conkeror.


To compliment the mobile we've got the stationary: parsing command line options with getopt, checking your Ruby code with metric_fu, and building a secure Squid proxy. How is this stationary you ask? What can we say? It's not. We just wanted to see if anybody actually read this part of the page :) .


All this and more, and all you have to do is get your hot sweaty hands on the latest copy of Linux Journal.





Read this issue