Google Answers Logo
View Question
 
Q: Need a Perl or shell (bash) script, or Netatalk tweak ( No Answer,   3 Comments )
Question  
Subject: Need a Perl or shell (bash) script, or Netatalk tweak
Category: Computers > Programming
Asked by: haversian-ga
List Price: $14.50
Posted: 06 Oct 2002 02:28 PDT
Expires: 15 Oct 2002 02:27 PDT
Question ID: 73157
I have a linux box running netatalk, which is letting Macintosh
clients violate the Macintosh (OS9.2, 8.x, 7.5.x) naming conventions. 
Specifically, since ext3 is case sensitive, netatalk allows filenames
differing only by case, but when the Mac tries to make use of these
files simultaneously (for example, by copying a folder containing both
foo and FOO from the server to the desktop), it complains that they're
the same file.

So, I need a way to configure Netatalk to disallow Mac clients from
creating files differing only by case.

OR

A Perl or bash script to clean up.  Specifically I want a script with
the following characteristics:
  Will accept one or more directory names as command line arguments
  Will resursively search through all files in the directories given
  When it locates two or more files in the same directory whose
filenames differ only by case, it will truncate the filenames of all
but the first duplicate file to 23 characters and append the following
string:  "DUP#nnnn" where nnnn is a zero-extended integer in the range
0-9999.  This number should be stored in a file (can be hard-coded in
the script - I'll change it to be where I want it), and should
increment by one each time a file renaming is performed.
  The script should operate efficiently.  I have a directory which may
contain 100,000+ files spanning 15GB, and it needs to be checked daily
- this is not an operation that can take 6 hours.

The files will not be changing while the script is running - reading a
directory worth of filenames into an array for sorting may be a good
way to start.
When the file containing nnnn reaches 9999, it should wrap over to
0000.
The order in which directories are scanned is unimportant.
Answer  
There is no answer at this time.

Comments  
Subject: Re: Need a Perl or shell (bash) script, or Netatalk tweak
From: gmuslera-ga on 07 Oct 2002 14:24 PDT
 
Why not make case insensitive the directory where the files are
stored? A way that this can be done is in example configuring samba to
export the "real" tree as case-insensitive, mount that share, and put
it as the netatalk exported dir, so the clients will be not worried
about duplicates filenames.
Subject: Re: Need a Perl or shell (bash) script, or Netatalk tweak
From: kyleha-ga on 08 Oct 2002 13:47 PDT
 
#!/usr/bin/perl

#
# This seems to work.  I just wrote it and tested it quickly.
#

use strict;
use File::Find;
use IO::File;
use IO::Dir;

my $counter_file = 'counter_file';
my $counter;
my $fh = new IO::File $counter_file;
if ( ! $fh ) {
    warn "can't read $counter_file: $!";
    $counter = 0;
}
else {
    $counter = <$fh>;
    $fh->close;
}

foreach my $dir ( @ARGV ) {
    check_dir( $dir );
}

$fh = new IO::File ">$counter_file";
if ( ! $fh ) {
    die "can't write $counter_file: $!";
}
print $fh "$counter\n";
$fh->close;

exit;

sub check_dir {
    my ( $dir ) = @_;

    print "check_dir( $dir );\n";

    my @dirs = ();
    my $dh = new IO::Dir $dir;
    my $fn;
    my %collide = ();
    my @tmp = ();
    while ( defined( $fn = $dh->read ) ) {
        push( @tmp, $fn );
    }
    undef $dh;

    foreach my $fn ( @tmp ) {
        my $file = "$dir/$fn";
        if ( -f $file ) {
            $fn =~ tr/A-Z/a-z/;
            if ( $collide{ $fn }++ ) {
                my $ending = next_counter();
                $fn = substr( $fn, 0, 23 ) if ( length( $fn ) > 23 );
                my $newname = "$fn\#$ending";
                rename( $file, "$dir/$newname" ) || die "can't rename
'$dir/$fn' to '$dir/$newname': $!";
            }
        }
        elsif ( -d $file ) {
            next if ( $file =~ m:(/\.\.?$|^\.\.?$): );
            push( @dirs, $fn );
        }
    }

    foreach my $subdir ( @dirs ) {
        check_dir( "$dir/$subdir" );
    }
}

sub next_counter {
    my $out = $counter++;
    $counter %= 10000;
    $out = "0$out" while ( length( $out ) < 4 );
    return $out;
}
Subject: Re: Need a Perl or shell (bash) script, or Netatalk tweak
From: haversian-ga on 09 Oct 2002 13:03 PDT
 
Thanks much for the script!

There's a bug somewhere that causes the first renamed file (each time
the script is run) to be named incorrectly - rather than get 0001
added, it's getting 001? - I think the question mark is actually \n,
but I'm not positive.  Only happens to the first file, only happens
when reading counter_file as opposed to generating a  new one because
it's missing.  This doesn't happen to later renamings, so I've
inelegantly fixed it using $counter++; $counter--; which does the
trick.  If I weren't a Perl newbie maybe I could do better than a
cosmetic solution.  *shrug*

Again, thanks - it seems to be working just fine on my test cases as
well.  This afternoon I'll run it on (a copy of) the real data.

Important Disclaimer: Answers and comments provided on Google Answers are general information, and are not intended to substitute for informed professional medical, psychiatric, psychological, tax, legal, investment, accounting, or other professional advice. Google does not endorse, and expressly disclaims liability for any product, manufacturer, distributor, service or service provider mentioned or any opinion expressed in answers or comments. Please read carefully the Google Answers Terms of Service.

If you feel that you have found inappropriate content, please let us know by emailing us at answers-support@google.com with the question ID listed above. Thank you.
Search Google Answers for
Google Answers  


Google Home - Answers FAQ - Terms of Service - Privacy Policy