#!/usr/bin/perl # rename_case_colliders # This script renames all the files supplied as command-line args # where necessary so that the filenames are distinct on case-insensitive # filesystems (e.g. OS X's HFS+) # If there are filenames that would collide on a case-insensitive filesystem, # they are renamed to be in lowercase and with numbers appended to make them # distinct. # E.g. "tesT", "Test" would be renamed to "test_001", "test_002" # # Cameron Hayne (macdev@hayne.net) Mar 2006 use warnings; use strict; sub parseFilename($) { my ($filename) = @_; use File::Basename; my ($name, $dir, $ext) = fileparse($filename, '\..*?'); $dir = "" if $dir eq "./"; return ($name, $dir, $ext); } sub proposeNewFilename($) { my ($filename) = @_; my ($name, $dir, $ext) = parseFilename($filename); my $lcName = lc($name); my $lcExt = lc($ext); my $num = "000"; my $proposedName; do { ++$num; $proposedName = "$dir$lcName" . "_$num" . $lcExt; } while (-e $proposedName); return $proposedName; } sub renameFile($) { my ($origName) = @_; my $newName = proposeNewFilename($origName); die "Proposed name \"$newName\" already used\n" if -e $newName; print "About to rename \"$origName\" to \"$newName\"\n"; rename($origName, $newName) or die "Failed to rename \"$origName\" to \"$newName\": $!\n"; } MAIN: { my %colliders; # each element in this hash is an array of colliding names chomp(@ARGV = ) unless @ARGV; foreach my $filename (@ARGV) { my ($name, $dir, $ext) = parseFilename($filename); my $lcName = lc($name); my $lcExt = lc($ext); push(@{$colliders{"$dir$lcName$lcExt"}}, $filename); } foreach my $key (keys %colliders) { my @filenames = @{$colliders{$key}}; next unless scalar(@filenames) > 1; foreach my $filename (@filenames) { renameFile($filename); } } }