#!/usr/bin/perl -w # # mv_renum [-#] filenames... # mv_reseq [-r] [-#|-#.#] filenames... # # Quick renumbering of the the LAST number in the filenames appropriately # ordering the sequence of renaming to ensure no file is overwritten during the # process. Files are only renamed if needed. # # # mv_renum # # Set the last number in each filename to a fixed width (2 by default, or the # width given in the -# option). This is done by adding and removing leading # "0"s to fit the width. A width of 1 (option -1) will just strip all leading # "0"s. # # For any other width the program will abort before doing anything if a number # is found that will NOT fit the width. # # # mv_reseq # # Re-sequence the last number in each file into accending order. The numerical # order of the files (not the alphabetical order) is preserved! By default the # numbered sequence starts at 1, and increments by 1. # -# Change the start number, and increment by 1 # -.# Set both start and increment to the same number given # -#.# Set start and increment numbers individually # The width of the number in the filenames are always resized to the maximum # width found in all filenames, or the width which can contain the largest # number. # # The -r option will reverse the ordering of the files (inverse reseq) # # Even if processing begins however, the program will NEVER overwrite an # existing file. # # # See Also # mv_perl Renames filenames based on a perl RE (or code) # rename Renames by replacing one string with another (Gnu-Linux) # mved Renames both sides of the variable part. # # Anthony Thyssen 5 June 2002 # ### use strict; #use warnings; # perl 5.6 and greater only. ( my $prog = $0 ) =~ s/^.*\///; my $renum = $prog =~ /renum/o; sub Usage { die(@_, "Usage: $prog ", $renum ? "[-#]" : "[-#|-#.#]", " filenames...\n"); } my %file; # the filename, split into parts, then rejoined into destination my %num; # the orginal number to filename map (file sequence order) my $verbose = 1; # what is the program doing (debugging) # ---------- Option handling ---------- # width or starting number and increment defaults my $n = my $i = 1; # resequence defaults my $w = 0; # no default given for width # number of digits to expand numbers in filenames to. my $reverse = ( $ARGV[0] eq '-r' ) ? shift : 0; if ( $ARGV[0] eq '--' ) { shift; } elsif ( $ARGV[0] =~ /^-(\d+)$/ ) { ( $renum ? $w : $n ) = $1; shift; } elsif ( $ARGV[0] =~ /^-(\d+)[,.](\d+)$/ ) { Usage("Increment on valid for 'reseq' - ABORTING\n") if $renum; $n = $1; $i = $2; shift; Usage("Increment zero or undefined - ABORTING\n") unless $i; } elsif ( $ARGV[0] =~ /^-[,.](\d+)$/ ) { Usage("Increment on valid for 'reseq' - ABORTING\n") if $renum; $n = $1; $i = $1; shift; Usage("Increment zero or undefined - ABORTING\n") unless $i; } elsif ( $ARGV[0] =~ /^-/ ) { Usage("Unknown option $ARGV[0]\n"); } unless ( @ARGV ) { #opendir(DIR,'.') || die "Can't open current directory"; #@filenames = grep { -f } readdir(DIR); #closedir(DIR); Usage("No filenames given!\n"); } # --------- initial file handling ---------- # Get all the filenames and file numbers and determine destination. my $lrg = 0; # largest length of the original file numbers my $big = 0; # biggest interger found in original file numbers foreach( @ARGV ) { die("Given filename \"$_\" does not exist! -- ABORTING\n") unless -e $_; my @p = /^(.*)(? $lrg; # largest original width $big = $num if $num > $big; # largest number found $num{$num} = $_; # sequencing order of filenames $file{$_} = [ @p ]; # save the 3 strings from filename } # ------------ work out destination names ----------- if ( $renum ) { # figure out best width for renumbering $lrg = length($big); # the best width for the numbers in given filenames $lrg = 2 if $lrg < 2; # minimum width (unless supplied) $w = $lrg if $w == 0; # width not given, so use the best width. } else { # figure out the width for resequencing $w = length( $n + $i * ( scalar(keys %num) -1) ); $w = $lrg if $lrg > $w; # never shrink any existing number in input files } # create the destination filename for each file for my $num ( sort { $reverse ? $b<=>$a : $a<=>$b } keys %num ) { $_ = $num{$num}; # filename for this number $n = $num if $renum; # the output number for this file # calculate the destination filename # NOTE: for renum preserve the existing width, if no width is defined # Replace the array of parts with the final destination filename $file{$_} = sprintf( "%s%0*d%s", $file{$_}[0], ($w||length($file{$_}[1])), $n, $file{$_}[2] ); $n += $i if !$renum; # increment the sequence if mv_reseq (eg: !renum) if ( $_ eq $file{$_} ) { # remove files with will not need renaming delete($num{$num}); # no need to rename this filename! delete($file{$_}); next; } } unless( %num ) { print STDERR "No filenames need to be renamed - DONE\n"; exit 0; } # check that we will not overwrite a file that we are not moving! for my $num ( sort { $a<=>$b } keys %num ) { $_ = $num{$num}; # original filename my $f = $file{$_}; # destination filename die("Unable to rename \"$_\" to existing file \"$f\" -- ABORTING\n") if !exists $file{$f} && -f $f; # destination exists? } # ------------ rename the files ----------- # Try to rename each file as we can. This is needed as during a re-sequence # operation, one filename may need to be renamed to a file not yet renamed # # Eg: file_55 renames to file_60, but file_60 will be later renamed to file_68 # A Worst case: rename files 1,2,3,4,5,... to files 2,3,4,5,6,... # Only one file (the last) can be renamed for each run of the inner loop. # while ( %num ) { my $file_was_moved = 0; # did we move a file -- stop infinite loops # for each file that is left to move - rename them in numerical sequence for my $num ( sort { $a<=>$b } keys %num ) { $_ = $num{$num}; # original filename my $f = $file{$_}; # destination filename next if -f $f; # can't move this file - YET! try again later print "mv \"$_\" \"$f\"\n" if $verbose; rename( $_, $f ) or die("rename \"$_\" \"$f\" failed: $!\n"); delete($num{$num}); # This file has been moved! delete($file{$_}); $file_was_moved = 1; } # FUTURE -- look for filename swaps if no file moved # note a swap should NOT actually be posible as files are sequenced! # Make sure we are doing at least one move per loop -- or we have problems! die("Unable to rename files...\n", join(' ', values %num), "\n --ABORTING\n") unless $file_was_moved; } print "DONE\n" if $verbose; exit(0);