#!/usr/bin/perl # splitdir: # This script moves the files from a specified directory # into several sequentially named sub-directories. # (This is intended for splitting up large directories.) # The first command-line argument specifies the directory to be split up. # The second command-line argument specifies the basename to be used for the # sequence of sub-directories. # (If no 2nd command-line argument is given, subDirBasename will be "subdir") # The third command-line argument specifies the maximum number of files to # be put in each sub-directory. # (If no 3rd command-line argument is given, maxFiles will be 100) # Sample usage: # splitdir ~/MyStuff stuff 200 # The above command would create sub-directories "stuff1", "stuff2", ... # and move the files (but not existing sub-directories) from ~/MyStuff # into those sub-directories in groups of 200. # If a sub-directory with one of those names already exists, that # sub-directory will be left as is and the next name in the sequence # used instead. # # Cameron Hayne (macdev@hayne.net) March 2009 use strict; use warnings; my $scriptName = "splitdir"; # used in error messages sub fatalError($) { my ($msg) = @_; print "$scriptName: $msg\n"; exit 1; } MAIN: { die "Usage: $scriptName dirName [subDirBasename [maxFiles]]\n" if scalar(@ARGV) < 1; my $dirName = shift @ARGV; fatalError("'$dirName' is not a directory") unless -d $dirName; fatalError("Insufficient permissions on '$dirName'") unless -r $dirName and -w $dirName and -x $dirName; my $subDirBasename = (@ARGV ? shift @ARGV : 'subdir'); fatalError("'$subDirBasename' is not an acceptable subDirBasename") unless $subDirBasename =~ /^[\w -]+$/; my $maxFiles = (@ARGV ? shift @ARGV : 100); fatalError("maxFiles must be a positive integer") unless $maxFiles =~ /^\d+$/ and $maxFiles > 0; # cd to the specified directory to make things easier chdir($dirName) or fatalError("Can't chdir to $dirName: $!"); my $fileCount = 0; my $subDirCount = 0; my $subDirNumber = 0; my $subDirName; opendir(DIR, '.') or fatalError("Can't opendir $dirName: $!"); while (defined(my $fileName = readdir(DIR))) { next if $fileName =~ /^\.\.?$/; # skip . and .. next if -d $fileName; # skip sub-directories if ($fileCount % $maxFiles == 0) { do { ++$subDirNumber; $subDirName = "$subDirBasename$subDirNumber"; } while (-e $subDirName); mkdir $subDirName or fatalError("Can't create sub-directory '$subDirName': $!"); ++$subDirCount; } # since we just created the sub-dir, there should be no possibility # of filename collision - but let's be paranoid: fatalError("There is already a file named '$fileName' - aborting") if -e "$subDirName/$fileName"; rename($fileName, "$subDirName/$fileName") or fatalError("Can't move file '$fileName' to " . "subdir '$subDirName': $!"); ++$fileCount; } closedir(DIR); print "Moved $fileCount files into $subDirCount sub-directories\n"; } # testing the script in Bash: # % mkdir LargeDir # % (cd LargeDir; for i in `jot 1000 1`; do touch $i; done) # % ls LargeDir | wc -l # 1000 # % splitdir LargeDir foo 100 # Moved 1000 files into 10 sub-directories # % ls LargeDir # foo1 foo10 foo2 foo3 foo4 foo5 foo6 foo7 foo8 foo9