Hey everyone,
I wrote a perl script to retrieve the 'Last Modified' timestamp of every file within the directory (including sub-directories) specified by the user, and output those file paths to an external file. The user can also specify which files will be outputted by the timestamp in days (e.g. output only files that have been modified within the last eight days), and the destination in which to store the external file.
The script I have written is performing as expected, with one exception: I get a 'invalid top directory at C:/Perl/lib/File/Find.pm line 598.' error when testing the script with certain directories in my C: drive. This occurs only with directories that contain no spaces (e.g., \drivers, \Perl, \WINDOWS, etc). Directories like "\Program Files", and "\Documents and Settings" do not bring up the error. I even created two folders, "\TestFolder", and "\Test Folder" (difference being a space), with the same files in each. As expected, "\TestFolder" gave me the error and "\Test Folder" did not. I also checked the permissions of all directories and I have full control of the directories that bring up the error, and the ones that do not.
I am using the module File::Find to go through all files/subdirectories of the user specified directory. I went to Find.pm and commented line 598, which actually fixed this problem, but of course this is the incorrect way of solving the problem.
Here is my code (See block of code in main() giving the error):
#!/usr/bin/perl -w # fileDateTime.pl # # Script is used to check the timestamp (last modified) of all files w +ithin the # specified folder, including all files in any subdirectories. The ti +mestamps will # also be used to provide information as to when the file was last mo +dified (in days) # relavent to the current time. The filenames, timestamps, and last mo +dification will # be outputted to a .xls file. # # Author: JHermida # Date : 9/15/2011 THU # Copyright (c) 2011, Cross Country Automotive Services, Inc. ###################################################################### +########## # ALWAYS USE STRICT AND WARNINGS ###################################################################### +########## use strict; use warnings; use Switch; use Cwd; ###################################################################### +########## # MODULES USED (from CPAN) ###################################################################### +########## use File::Find; use File::Copy; use Spreadsheet::WriteExcel; ###################################################################### +########## # MODULES USED (written in-house) ###################################################################### +########## ###################################################################### +########## # GLOBAL VARIABLES ###################################################################### +########## my $num_args = $#ARGV + 1; my $maxDays = -1; # Filters which files will be outputted based on how + old the last modification to the file has been made. '-1' denotes al +l files will be outputted. my $maxDays_ptr = \$maxDays; my $numOfFiles = 0; # Stores the number of files found based on $maxDa +ys my $numOfFiles_ptr = \$numOfFiles; my $workbook = Spreadsheet::WriteExcel->new('timestamps.xls'); my $sheet = $workbook->add_worksheet("Timestamps"); my $columnTitleFormat = $workbook->add_format( bold => 1, size => 12, +); my $rowPos = 1; my $fileCol = 0; my $dateCol = 1; my $dayCol = 2; ###################################################################### +########## # SUBROUTINES DECLARED ###################################################################### +########## sub main(); sub processTimestamp(); ###################################################################### +########## # MAIN LOOP ###################################################################### +########## exit main(); ###################################################################### +########## # SUBROUTINES DEFINED ###################################################################### +########## #--------------------------------------------------------------------- +---------- # main() # # PURPOSE: Main loop # # INPUT: Directory to check modification dates of files # # OUTPUT: List of files and modification dates to timestamps.xls # # SIDE EFFECTS: #--------------------------------------------------------------------- +---------- sub main() { my $inputDir = ''; my $outputDir = ''; # Check arguments passed in by user switch ($num_args){ case 0 { print "\nERROR: No arguments were passed. Command aborted!\n"; help(); } case 1 { chomp($inputDir = $ARGV[0]); #$inputDir =~ s/\\/\//; # Check if the passed directory exists in the system if ( -d $inputDir ) { $outputDir = getcwd; # Output directory will be the pwd } else { print "\nERROR: Invalid input. The input directory may be +non-existent. Command aborted!\n"; help(); } } case 2 { chomp($inputDir = $ARGV[0]); # $inputDir must be a valid directory if ( !(-d $inputDir) ) { print "\nERROR: Invalid input. Command aborted!\n"; help(); } # Check if the second argument is an integer (specifying max d +ays) or a directory (specifying the output directory) if ( $ARGV[1] !~ /\D/ ) { # if 2nd argument is an integer $$maxDays_ptr = $ARGV[1]; $outputDir = getcwd; # Output directory will be the pwd } elsif ( -d $ARGV[1] ) { $outputDir = $ARGV[1]; } else { print "\nERROR: Invalid input. The input and/or destinatio +n directory may be non-existent, or the specified number of days may +be invalid. Command aborted!\n"; help(); } } else { # 1st and 3rd arguments must be existing directories, and 2nd +argument must be an integer # Any additional arguments will be ignored if ( -d $ARGV[0] && -d $ARGV[2] && $ARGV[1] !~ /\D/) { chomp($inputDir = $ARGV[0]); chomp($$maxDays_ptr = $ARGV[1]); chomp($outputDir = $ARGV[2]); } else { print "\nERROR: Invalid input. The input and/or destinatio +n directory may be non-existent, or the specified number of days may +be invalid. Command aborted!\n"; help(); } } } ################################################################## +########## # This section gives the "invalid top directory at C:/Perl/lib/Fil +e/Find.pm line 593" # error when attempting to scan a directory (with no spaces) in th +e C: drive. Without # this section, the script will enter an infinite loop until the u +ser terminates # # Section removed 9/19, then added back 9/20 - JAH ################################################################## +########## #Get all files in folder specified, add only directories for searc +h my @directories_to_search = (); my @files = <"$inputDir"*>; foreach my $file ( @files ) { # If the file is a directory, add to @directories_to_search if ( -d $file ) { push ( @directories_to_search, $file ); } } ################################################################## +########## # Format excel sheet $sheet->write(0, 0, 'File path', $columnTitleFormat); $sheet->write(0, 1, 'Last Modified (Date)', $columnTitleFormat); $sheet->write(0, 2, 'Last Modified (Days)', $columnTitleFormat); $sheet->set_column('A:A', 150); $sheet->set_column('B:B', 30); $sheet->set_column('C:C', 25); # Iterate through files/subdirectores. See File::File module find(\&processTimestamp, @directories_to_search);#$inputDir); # Check if any files were scanned if ( $$numOfFiles_ptr eq '0' ) { $sheet->write($rowPos, $fileCol, "No files found!"); $sheet->write($rowPos, $dateCol, "N/A"); $sheet->write($rowPos, $dayCol, "N/A"); } # Close workbook $workbook->close(); # Print number of files found that were modified based on user inp +ut switch ( $$maxDays_ptr ) { case -1 { print "\n[SUCCESS]: $$numOfFiles_ptr files were scanned fo +r their modification date"; } case 0 { print "\n[SUCCESS]: $$numOfFiles_ptr files were modified t +oday"; } case 1 { print "\n[SUCCESS]: $$numOfFiles_ptr files were modified i +n the last day"; } else { print "\n[SUCCESS]: $$numOfFiles_ptr files were modified i +n the last $$maxDays_ptr days"; } } # Copy output file if the user has specified a destination if ( $outputDir ne getcwd ) { if ( copy( "timestamps.xls", "$outputDir"."/timestamps.xls" ) +) { print "\n[SUCCESS]: See $outputDir/timestamps.xls\n"; } else { print "\n[FAIL]: Failed to copy timestamps.xls to $outputD +ir"; } } else { print "\n[SUCCESS]: See " . getcwd . "/timestamps.xls\n" } } #--------------------------------------------------------------------- +---------- # processTimestamp() # # PURPOSE: gets the modification timestamp and number of days all file +s were last # modified within the target directory # # INPUT: n/a # # OUTPUT: prints information to timestamp.xsl file # # SIDE EFFECTS: #--------------------------------------------------------------------- +---------- sub processTimestamp() { # If file, check modification date #print $_."\n"; if ( !(-d $_) ) { # Get modification date my $mod_date = localtime( (stat $_)[9] ); # Get modification in days my $mod_days = -M $_; # Format of modification date is a decimal, therefore, truncat +e for whole # $mod_days = sprintf("%d", $mod_days); # Output to excel sheet if ( $$maxDays_ptr >= $mod_days || $$maxDays_ptr eq '-1') { $sheet->write($rowPos, $fileCol, $File::Find::name); $sheet->write($rowPos, $dateCol, $mod_date); $sheet->write($rowPos++, $dayCol, $mod_days); } # Count number of files found if maxDays is specified by user # If maxDays was not specified, count total number of files sc +anned if ($$maxDays_ptr >= $mod_days && $$maxDays_ptr ne '-1' || $$m +axDays_ptr eq '-1') { $$numOfFiles_ptr++; } } } #--------------------------------------------------------------------- +---------- # help() # # PURPOSE: gives help, then exits with errorlevel set to 1. # # INPUT: n/a. # # OUTPUT: Prints help. # # SIDE EFFECTS: n/a #--------------------------------------------------------------------- +---------- sub help() { print <<EOF; ---------- Usage: dateTime.pl <Input Directory*> <Max number of days> <Output des +tination> * Indicates the field is required. The "<Input Directory>" (without <>) argument specifies the root path +of the directory in which to retrieve the modification date of each f +ile within the directory, including all files within each subdirector +y. The "<Max number of days>" argument filters which files will be output +ted based on how old the last modification to the file has been made. + This value must be an integer. Enter '0' to see files modified toda +y, '1' to see files modified in the last 24 hours, etc. Leave this fi +eld blank to see all file modification dates. The "<Output destination>" argument specifies the directory in which t +he output file will be created. If this value is not specified, the +file will be created in the present working directory. NOTES: 1. If the path contains any spaces, it must be enclosed with parenth +eses. 2. The script will determine whether the second argument specifies t +he max number of days or the output destination. ---------- EOF exit 1; }
Any help and/or feedback would be much appreciated.
Thanks,
JHermida
| For: | Use: | ||
| & | & | ||
| < | < | ||
| > | > | ||
| [ | [ | ||
| ] | ] |