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

I am currently writing a script to compare file time stamps. I currently have it pulling and displaying time stamps but cannot get it to compare them. I think I may be making it more complicated than it needs to be or just looking at it in the wrong light. I am very new to perl so any help is greatly appreciated. Thank you in advance.
#use strict; $comparetime="0"; sub compare; $Clinrxdir="DIRECTORY GOES HERE"; opendir(DIR,$Clinrxdir); @Files= readdir(DIR); closedir(DIR); foreach $cpupdate (@Files){ if($cpupdate =~ m/\s*cp_/i){ my $m_time = (stat($Clinrxdir.$cpupdate))[9]; my ($i_wday, $i_month, $day, $year) = (localtime($m_time))[6,4,3,5]; compare($m_time); print "Timestamp for " . $cpupdate . " " . $m_time ."\n"; } } print "This is a test of file = " . $file . "\n"; system("pause"); sub compare($){ local( $string ) = shift; if($comparetime > $string){ $file=$cpupdate;} else{$compare=$m_time;} }

Replies are listed 'Best First'.
Re: Compare script
by stevieb (Canon) on Aug 29, 2009 at 18:08 UTC

    I don't have much time at the moment to review the code specifically, but hopefully what I've done may help a bit.

    I think that you should be passing your args into the sub, instead of using global vars. This causes much grief, beyond the problems it would cause if you wanted to move the sub to a library file. Also, prototypes within Perl functions don't do what most think they do. I can't think of a single link that describes this, but the archives, and Google explains it well

    I quickly put the following working code snip together to hopefully give you some ideas. It was hard to tell what you meant by "it wouldn't compare them" without knowing what you expected as a result:

    #!/usr/bin/perl use strict; use warnings; my $mtime = ( stat ( "/home/steve/devel/ISP/lib/ISP/User.pm" ))[9]; my $other_mtime = 999999999; my $cmp_result = compare ( $mtime, $other_mtime ); print "$cmp_result\n"; sub compare { my ( $mtime, $other_mtime, ) = @_; my $cmp_result; if ( $mtime > $other_mtime ) { $cmp_result = "mtime_is_higher"; } else { $cmp_result = "mtime_is_lower"; } return $cmp_result; }

    Hope it helps a bit

    Steve

      Thank you to all who helped. Your guess was indeed correct I was looking at the dir attempting to see what file was newest and then output that file name. I have the perl working, the code may not be the nicest but it works. I realised I was not keeping the old compare time when looking at graff's example. I unfortunately cannot use a lot of modules. We are running an older version of perl at work and they refuse to update. I can't even use NET::Telnet Here it is as it works followed by the output:
      #use strict; my $comparetime="0"; sub compare; ##defines locations to look for files $Clinrxdir="D:\\StoreAccess\\shared\\sup shared\\"; ##reads DIR opendir(DIR,$Clinrxdir); @Files= readdir(DIR); closedir(DIR); #for every file retured do the following foreach $cpupdate (@Files){ ##if the file starts with CP_ checks it's timestamp if($cpupdate =~ m/\s*cp_/i){ ##I did not write this section I was using code I read up on using goo +gle and found in other various ##scripts we used, for me the fact it broke it down in to a simple int + style var made life easier. my $m_time = (stat($Clinrxdir.$cpupdate))[9]; my ($i_wday, $i_month, $day, $year) = (localtime($m_time))[6,4,3,5]; ##prints what files where found and time stamps print "Timestamp for " . $cpupdate . " " . $m_time ."\n"; ##Attempting to use this sup to compare time stamps and change the $fi +le var to the one with most recent ##stamp compare($m_time); } } ##$file is pulled from the compare sub print "The oldest file= " . $file . "\n"; print "The time stamp for this file is " . $oldcomp . "\n"; system("pause"); ##this was meant to work in the following manner: look at time stamp. +if it's older than the one ##currently stored do nothing, if it's newer change $file to be the fi +le name of the newer file. ##this is where I realised my folly thanks to graff's code.. I was not + keeping anything to compare to ##other than the new string sub compare($){ local( $string ) = shift; if($comparetime > $oldcomp){ $file=$cpupdate; $oldcomp=$string;} else{$comparetime = $string;} }

      Timestamp for CP_APR09.exe 1241465790
      Timestamp for CP_FEB09.exe 1236031283
      Timestamp for CP_JUL09.exe 1249498031
      Timestamp for CP_JUN09.EXE 1247593421
      Timestamp for CP_MAR09.EXE 1238771856
      Timestamp for CP_MAY09.EXE 1244056821
      The oldest file= CP_JUL09.exe
      The time stamp for this file is 1249498031

      That is as the output looks when returned in perl once again thank you for your help
Re: Compare script
by bichonfrise74 (Vicar) on Aug 29, 2009 at 18:02 UTC
    I have not look at your code in depth. But I think Date::Calc can help. Also, doing some super search will yield that this topic has been discussed several times Compare Time. In particular, take a look at this node node. See if it helps.
Re: Compare script
by toolic (Bishop) on Aug 29, 2009 at 18:06 UTC
    It's not clear to me what you are trying to do. Can you create some code that anyone can run to duplicate your problem? Sprinkle some more prints in your code, and show us the output.

    One way to check if a file is old is to use -M.

Re: Compare script
by TheRedcoat (Novice) on Aug 29, 2009 at 18:02 UTC
    #use strict; $comparetime="0"; sub compare; $Clinrxdir="D:\\StoreAccess\\shared\\sup shared\\"; opendir(DIR,$Clinrxdir); @Files= readdir(DIR); closedir(DIR); foreach $cpupdate (@Files){ if($cpupdate =~ m/\s*cp_/i){ my $m_time = (stat($Clinrxdir.$cpupdate))[9]; my ($i_wday, $i_month, $day, $year) = (localtime($m_time))[6,4,3,5]; compare($m_time); print "Timestamp for " . $cpupdate . " " . $m_time ."\n"; } } print "This is a test of file = " . $file . "\n"; print $file; system("pause"); sub compare($){ local( $string ) = shift; if($comparetime > $string){ $file=$cpupdate;} else{$comparetime=$string;} }
    I noticed some error's i made with my variable names, I corrected these, it now outputs the oldest timestamp, but does not update the name of the file to go with it.. for instance my timetamp was for file 4 but it was still listing file 1 as the output

      I went through the code, making changes mostly for practice. It is *untested*. Hopefully the comments are of use. Furthermore, hopefully others can criticize me for my deficiencies:

      #use strict; # don't disable strict. Period. # you are only using $comparetime within the compare() function # If you define it here, then it should be passed to the sub as # a parameter my $comparetime="0"; # sub compare; # no need for this my $Clinrxdir="D:\\StoreAccess\\shared\\sup shared\\"; opendir(DIR,$Clinrxdir); my @Files= readdir(DIR); closedir(DIR); for my $cpupdate (@Files){ if($cpupdate =~ m/\s*cp_/i){ my $m_time = (stat($Clinrxdir.$cpupdate))[9]; # what does a print() say about $m_time here? my ($i_wday, $i_month, $day, $year) = (localtime($m_time))[6,4 +,3,5]; # why are you breaking $m_time up. Depending on what you want to d +o, # it may be easiest to simply compare $m_time as an int my $comparison = compare($orig_mtime, $cmp_mtime $cupupdate); print "Timestamp for " . $cpupdate . " " . $m_time ."\n"; } } # I'm lost as to where $file comes from here... print "This is a test of file = " . $file . "\n"; print $file; system("pause"); sub compare(){ my ( $orig_mtime, $cmp_mtime, $cpuupdate ) = @_; if($cmp_mtime > $orig_mtime){ my $file = $cpupdate; } else{ #else{$comparetime=$string;} # it looks like you are trying to cause more than one side # effect with your subroutine, as opposed to focusing on providing # a single return. Personally, I've learned that mixing function # programming with global data will lead to bugs that are very # difficult to track down. # if you want to set $comparetime to $string, you should do it # outside of this routine, after you've collected up compare() # result } }

      Steve

      You say you want to "compare file time stamps", but you don't say what the goal is. Based on looking at your "compare" sub, it would seem that you are trying to identify the most recent file in a directory.

      First, you have to remember that readdir returns every directory entry, including "." and ".." (which are directories, as you must know, and which could often, by coincidence, be dated more recently than any data file in the directory).

      If you want to pick the most recent entry in a directory (not including "." or ".."), then try something like this:

      #!/usr/bin/perl use strict; use File::Spec; my $Usage = "Usage: $0 pathname\n"; die $Usage unless ( @ARGV == 1 and -d $ARGV[0] ); my $path = shift; opendir( D, $path ) or die "$path: $!\n"; my %files; for my $file ( grep !/^.{1,2}$/, readdir D ) { my $filepath = File::Spec->catfile( $path, $file ); my $file_age = -M $filepath; $files{$file_age} = $file; } my @age_order; push @age_order, $files{$_} for ( sort {$a<=>$b} keys %files ); print "Newest file in $path is: $age_order[0]\n";
      In that version, you just have to provide the directory path as a command line arg; it uses -M (as suggested by toolic below) to get the age of each file (as a floating point value, in days) relative to the time when the script starts running.