ManicMantis has asked for the wisdom of the Perl Monks concerning the following question:

Ok. I'm brand new to Perl and I'm hoping someone could assist me with what appears to be an overly ambitious project for a beginner. I'm running Windows and I want to automate the task of renaming a mass of log files based on their last modified date, a string from within the file, and a number to prevent duplicates, in the following format:

'string, date, number.log'

I would appreciate any help at all, from a push in the right direction to code snippets to whatever. I want to learn this, but I'm rather stuck at the moment. Thanks in advance.

Replies are listed 'Best First'.
Re: Modified Date, file renaming
by azatoth (Curate) on May 16, 2001 at 15:07 UTC
Re: Modified Date, file renaming
by Mungbeans (Pilgrim) on May 16, 2001 at 16:13 UTC
    The following code was used to delete log files older than x days. You should be able to modify it for your purposes. It's not state of the art (pretty old and designed to be supported by non perl experts so don't take it as gospel).

    ### ------------------------------- ### Read local directory opendir LOCALDIR, $location || croak "Unable to open: $location because $!"; @local_files = readdir LOCALDIR; foreach $file (@local_files){ ### Don't try to delete directories if ($file eq '.' || $file eq '..' || -d "$location$file" ) { next; } ### Test the age of each file in the directory and delete ### if too old (N.B. need to prepend directory to find file an +d ### use unlink for OS portable deletion). if ( -M "$location$file" <= $age) { $p_debug && print "$file is ok, only ", -M "$location$file", " days old\n"; } else { $p_debug && print "$file is too old, it's ", -M "$location$file", " days old\n"; unlink("$location$file") || croak "Unable to delete: $location$file because: $!"; } }

    You could use something like:

    ### Move each file into archive directory move( "$location$file", "$target$file") || croak "Unable to move: $location$file because: $!";

    ... the above to move files around. You'll need to rename the $file for the target however.

      Look out! If you're going to use the "|| die" syntax instead of "or die", you need to put parentheses around the arguments to opendir.

      opendir (FOO, $bar) || die "No dice";
      and
      opendir FOO, $bar or die "No dice";
      are equivalent: they try to open the dirhandle FOO to the directory listed in $bar, and if opendir fails, they die. But
      opendir FOO, $bar || die "No dice";
      is equivalent to
      opendir FOO, ($bar || die "No dice");
      which means that die is only called if $bar is empty, not if opendir fails. Which is unlikely to be what you intended.



      If God had meant us to fly, he would *never* have give us the railroads.
          --Michael Flanders

        Thanks - well spotted.

        $clean_up_code_before_posting || die "red face: $!";

        Must remember to clean up before I post (I now use or die)...

Re: Modified Date, file renaming
by Anonymous Monk on May 16, 2001 at 18:30 UTC
    Here is some bare bones code and a couple of subs from my toolbox. You may find these subs useful to generate unique filenames, and input scripts. You will also see a use of the -e $file syntax. -e checks for existance of a file -d for a dir, -s returns size, -M is days since last modification - see the perlop documentation c:\perl\html\index.html if you have Activestate perl. Look up the functions rename opendir readdir in perlfunc. Regular expression are in perlre. The FAQs are excellent and extensive. Tell us how you get on.
    #!/usr/bin/perl -w <--- this does nothing on win 32 use strict; use warnings; use diagnostics; require 5.6.0; # use / as the path delimiter perl will change it to \ for you # if you use \ you will regret it as this is the escape char # thus c:\thisdir is seen by perl as c:[tab]hisdir as \t is tab my $path = 'c:/logfiles/'; my $file; print "Processing...\n"; opendir(DIR,$path) or die "Unable to open dir $path: $!\n"; my @everything = readdir DIR; closedir DIR; foreach $file(@everything) { next if -d "$path$file"; # skip the directories next unless $file =~ m/\.log$/i; # look for files that end in .log print "Loading $path$file\n"; # if you get to here you have a logfile so slurp it up my @file = &get_file("$path$file",1); # now have lines of file in @ file so iterate through them LINE: for my $line(@file) { # look for whatever you are looking for # call the do stuff sub if you find a foobar if ($line =~ m/foobar/) { &do_stuff(@file); last LINE; # exit from this loop on $file } } } print "Done!\n"; exit; sub do_stuff { my @file = @_; #do you stuff to $file here (the file is in @file if you want it) print "Doing stuff to $path$file\n"; return; } # this sub makes a unique filename by adding numerals to the end of th +e passed # path/filename until it finds one that does not exist or dies after 1 +01 tries # useage: $my_unique_name = &make_unique_filename($filename); sub make_unique_filename { my $filename = shift; no warnings; while (-e $filename) { $filename =~ s/^(.*?)(\d*)$/$1.eval(($2||0)+1)/e; die "Unable to make unique file $filename(0..100) after 101 trie +s!\n" if $2 > 100; } return $filename; } # this sub inputs the script spceified by $path, and unless the $no_ba +ckup # argument is set to true it writes and verifies a backup # useage: @file = &get_file($part,$no_backup); sub get_file { # get arguments my $path = shift; my $no_backup = shift; # get script to process open (FILE, "<", "$path") or die "Unable to open file $path $!\n"; my @file = <FILE>; close FILE; # we will write a backup unless this has been specifically outlawe +d unless ($no_backup) { # make a uniquely named backup file $backup_file = "$path.bak"; $backup_file = &make_unique_filename($backup_file) if -e $back +up_file; # write script to script.bak before proceeding open (FILE, ">", $backup_file) or die "Unable to write backup +file $backup_file $!\n"; for (@file) { print FILE $_; } close FILE; # varify backup is a perfect copy, rampant paranoia is OK beca +use ..it happens open (BACKUP, "<", $backup_file) or die "Backup file failure, +aborting! $!\n"; my @backup = <BACKUP>; close BACKUP; for my $i (0..$#backup) { die "Backup file corrupt, aborting!\n" unless $file[$i] eq + $backup[$i]; } print "Backup of original script written to $backup_file\n"; } return @file; # if all has proceeded as expected we return the scrip +t }
    tachyon
this kinda what you're looking for?
by cforde (Monk) on May 17, 2001 at 00:22 UTC
    You might want to translate the dates into something more human readable and change the string extracted from the files, but here's a start:
    use FileHandle; my $directory = 'c:\temp'; my @logfile; my $handle; my %number; my $date; my $string; opendir LOGDIR, $directory or die "Unable to open $directory: $!"; @logfile = grep {/\.log$/} readdir(LOGDIR) # only .log files or die "unable to read $directory: $!"; foreach my $logfile (@logfile) { unless ($handle = new FileHandle "<$directory/$logfile") { print "Unable to open $logfile: $!\n"; next; } unless (read $handle, $string, 5, 0 ) { # get first 5 bytes print "unable to read $logfile: $!\n"; close $handle; next; } $date = (stat $handle)[9]; # get modify time close $handle; $number{"$string,$date"}++; print "$logfile --> $string,$date," . $number{"$string,$date"} . " +.log\n"; rename "$directory/$logfile", "$directory/$string,$date," . $numbe +r{"$string,$date"} . ".log" or print "unable to rename $logfile: $!\n"; }
    Have fun,
    Carl Forde

      It's great, but I run into a couple of problems. On a Windows 2k box I get an error saying the files can't be renamed because I don't have access (logged on as admin, full control and ownership of all the files in question). And on a Windows ME box it tells me they're unable to be renamed because there is 'No such file or directory.'

      I wasn't kidding when I said I was brand new to all of this, so attempts to fix it have been less than successful. Here's what I have so far, be gentle:

      
      use FileHandle;
      
      my $directory = 'c:\temp';
      my @logfile;
      my $handle;
      my %number;
      my $date;
      my $string;
      
      opendir LOGDIR, $directory
          or die "Unable to open $directory: $!";
      @logfile = grep {/\.log$/} readdir(LOGDIR)        # only .log files
          or die "unable to read $directory: $!";
      
      foreach my $logfile (@logfile) {
          unless ($handle = new FileHandle "<$directory/$logfile") {
              print "Unable to open $logfile: $!\n";
              next;
          }
      
          unless (read $handle, $string, 5, 0 ) {     # get first 5 bytes
              print "unable to read $logfile: $!\n";
              close $handle;
              next;
          }
          $date = (stat($handle))9;
          $rdate = (localtime($date));
          close $handle;
          $number{"$string,$rdate"}++;
          print "$logfile --> $string, $rdate " . '(' . $number{"$string,$rdate"} . ')' . ".log\n";
          rename "$directory/$logfile", "$directory/$string,$rdate" . $number{"$string,$rdate"} . ".log"
              or print "unable to rename $logfile: $!\n";
      }
      

      I also want to thank everyone for their help, it's a learning experience and I appreciate everyone's contribution. Just looking forward to the time when I'll be the one offering assistance.