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

Hi Monks, If I may ask your help

I have the following that is reading the contents of a CD, I have ten files on the CD but the following code is giving me a listing of those files 10 times, it's been driving me nuts the last few days trying to figure out why when the exact code is reading the contents of a mounted ISO just fine.

use strict; use warnings; package isomake; use Fcntl; our $package = 'ISOMake'; local $| = 1; our $drive = $ARGV[0]; our $P00 = $ARGV[1]; our $path = $ARGV[2]; if(!defined $ARGV[0]){ print "No drive letter Specified!\n"; exit; } if(!defined $ARGV[1]){ print "No item ID specified!\n"; exit; } if(!defined $ARGV[2]){ print "No path specified!\n"; exit; } our $DIR = undef; opendir($DIR,$drive) or die "Could not open $drive, Error :$!\n"; our @dirlist = sort readdir $DIR; close $DIR; undef $DIR; @dirlist or die "No Directories found in $drive\n"; our @badfilelist=(); our $nP00 = 0; our $nbad = 0; our $problem = 0; #my $KB = 1024; my $KB = 1000; #my $MB = 1048576; my $MB = 1000000; #my $GB = 1073741824; my $GB = 1000000000; print "\n***************Looking for Files and Directories************* +**\n"; foreach my $item (@dirlist) { #Read the source directory my @sfiles = (); + # List of files in the source my $ssize = 0; + # Count of file size my @dirlisting = (); my $ndir = 0; $dirlisting[0] = $drive; my $problem = 0; print "\n*****************Checking drive $drive*************** +**\n"; FILE: foreach my $file (@dirlisting) { + # Look at each file print "\nChecking for files and folders in $drive\n"; if (not -e $file) { print "$item seems to have an error\n"; $problem++; next FILE; } my $DDIR = undef; if (not opendir($DDIR,$file)) { + # Open the directory print "Could not open Directory $item, Error: $!\n"; $problem++; next FILE; } my @ddir = readdir $DDIR; + # Find out what is there close $DDIR; DIR: foreach my $dfile (@ddir) { + # And add it to the list if ($dfile =~ qr{^\.\.*$}) {next DIR;}; my $dpath = $file . $dfile; if (-d $dpath) { $dpath .= '\\'; push @dirlisting,$dpath; print "Found directory $dpath\n"; $ndir++; next DIR; } my $file = undef; if (not open($file,'<',$dpath)) { + # Check you can read the file print "You cannot open $dpath, Error: $!\n"; $problem++; next DIR; } my $x = ''; my $rsize = 0; while (my $bytes = sysread($file,$x,32768,O_BINARY)) { if (not defined $bytes) { print "Error Reading File $dpath: $!\n"; $problem++; } $rsize += $bytes; } close $item; my $fsize = (stat($dpath))[7]; + # How big is it? if (not($fsize == $rsize)) { print "Byte Check Error in $dpath, Counted $rsize +bytes, expected $fsize bytes\n"; $problem++; } $ssize += $fsize; push @sfiles,$dpath; + # Add it to the list of files to show print "Found file: $dpath, size = $fsize bytes\n"; } } #$problem and die "Found $problem problems examining ISO $driv +e mounted on drive $drive\n"; my $nfiles = @sfiles; print "\nFound $ndir directories on drive $drive\n"; if ($ssize < $KB) { print "Found $nfiles files on drive $drive, total size is a +pprox $ssize bytes in size!\n"; } elsif (($ssize > $KB) && ($ssize < $MB) ) { $ssize = $ssize / $KB; my $ffsize = sprintf "%.2f",$ssize; print "Found $nfiles files on $drive, total size is approx +$ffsize KB in size!\n"; } elsif ( ($ssize > $MB) && ($ssize < $GB) ) { $ssize = $ssize / $MB; my $ffsize = sprintf "%.2f",$ssize; print "Found $nfiles files on drive $drive, total size is a +pprox $ffsize MB in size!\n"; } else { $ssize = $ssize / $GB; my $ffsize = sprintf "%.2f",$ssize; print "Found $nfiles files on drive $drive, total size is a +pprox $ffsize GB in size!\n"; } } if ($nbad) { open (BADLIST, ">$path/BadFileList.txt") or die "$! error trying t +o overwrite"; print BADLIST "****BAD FILE LISTING****\n"; foreach my $L (@badfilelist) { print BADLIST "$L\n"; } print BADLIST "\n$nbad possible bad images"; close BADLIST; } print "\nFound $nbad bad file/s";

I'm thinking I may be able to achieve what I want perhaps with File::Find the start of my code is below.

use strict; use warnings; use File::Find qw(find); #Use File::Find module #no warnings 'File::Find'; #This turns off the warnings for File::Find + e.g. Folder access issues. our $drive = $ARGV[0]; if(!defined $ARGV[0]){ print "No drive letter Specified!\n"; exit; } #*****************SET Variables************************ our $drivepath = $drive; our @drivelisting=(); #Initialise the array #******************************************************* find(\&all, $drivepath); foreach my $p (@drivelisting) #For each line (file/folder found) $ +p in the array @drivelisting { print "$p\n"; #Print each line in the array to screen } sub all {#Sub routine for File::Find. on all locations my $fn = $File::Find::name; push @drivelisting, $fn if($fn ne "$drive"); }

Replies are listed 'Best First'.
Re: Optical drive contents listing problem
by Athanasius (Archbishop) on Feb 22, 2015 at 06:39 UTC

    I have looked at the first script only. It contains the following:

    foreach my $file (@dirlisting) { ... DIR: foreach my $dfile (@ddir) { ... if (-d $dpath) { ... push @dirlisting,$dpath; ... } ... } ... }

    pushing to array @dirlisting while iterating through it in an (outer) foreach loop is a recipe for trouble. From perlsyn#Foreach-Loops:

    If any part of LIST is an array, foreach will get very confused if you add or remove elements within the loop body, for example with splice. So don't do that.

    Hope that helps,

    Athanasius <°(((><contra mundum Iustus alius egestas vitae, eros Piratica,

Re: Optical drive contents listing problem
by RichardK (Parson) on Feb 22, 2015 at 13:21 UTC

    File::Find::Rule is friendlier interface to Find::File, and it has lots matching rules. For example, you can easily find all empty files.

    So you could do something like this :-

    my @dirs = Find::File::Rule->directory()->in($drive); my @files = File::Find::Rule->file()->in($drive); my @empty = File::Find::Rule->file()->empty()->in($drive);
Re: Optical drive contents listing problem
by Anonymous Monk on Feb 22, 2015 at 09:28 UTC
    readdir is low-level, avoid it , use Path::Tiny
    use Path::Tiny qw/ path /; my @files = path( 'anypath' )->children( qr/^[a-z].+\.txt$/ );
    fion
      "readdir is low-level, avoid it, use Path::Tiny"

      After having another Déjà vu because you post this more often i decided to stop being ignorant and read the manual of Path::Tiny. This is cool stuff.

      And i think the links to similar modules the author provides, are also very, very useful.

      Let me cite from the docs of Path::Iterator::Rule, if you aren't the author ;-)

      use Path::Iterator::Rule; my $rule = Path::Iterator::Rule->new; # match anything $rule->file->size(">10k"); # add/chain rules # iterator interface my $next = $rule->iter( @dirs ); while ( defined( my $file = $next->() ) ) { ... } # list interface for my $file ( $rule->all( @dirs ) ) { ... }

      And IMHO The Real Thing is:

      $rule->contents_match(qr/BEGIN .* END/xs); $rule->line_match(qr/^new/i, qr/^Addition/);

      Thank you very much for pointing me to this stuff and best regards, Karl

      «The Crux of the Biscuit is the Apostrophe»