Jonathan Dewein
Jonathan Dewein

Reputation: 984

Create an array of hashes describing files obtained from a given directory

I need to

  1. Read a list of the files from in a specified directory

  2. Create an array of hashes describing those files

My program fetches the path to a directory from the command line, opens it and reads its contents. Directories and "dot" files are skipped, and the absolute path to every other entry is printed.

use strict;
use warnings;

use Data::Dumper qw(Dumper);
use File::Spec;
use Digest::SHA qw(sha256_hex);

my $dir = $ARGV[0];

opendir DIR, $dir or die "cannot open dir $dir: $!";

while ( my $file = readdir DIR ) {
    next unless ( -f "${ \File::Spec->catfile($dir, $file) }" );
    next if ( "$file" =~ m/^\./) ;
    print "${ \File::Spec->rel2abs($file) }\n";
}

closedir DIR;

Here I take a single file and create a hash with the path, size, and sha256sum.

my $file = "file1.txt";

my @fileref = (
    {
        path    =>  "Full path: " . File::Spec->rel2abs($file),
        size    =>  "Size (bytes): " . -s $file,
        id      =>  "SHA256SUM: " . sha256_hex($file),
    },
);

print "\n";
print "$fileref[0]{path}\n";
print "$fileref[0]{size}\n";
print "$fileref[0]{id}\n";

All of this works, but I cannot figure out how to iterate over each file and add it to the array.

This is what I planned

for each file
    push file into array
    add the path, size, and id key:value pairs to file
repeat

How can I generate the necessary array?

Upvotes: 1

Views: 208

Answers (3)

Borodin
Borodin

Reputation: 126722

Here's my approach to a solution. I've saved a lot of code by using File::Spec::Functions instead of File::Spec, and calling rel2abs only once

I've also removed the labels, like "Full path: ", from the values in the hashes. There's no reason to put presentation strings in there: that's for the output code to do

use strict;
use warnings;

use File::Spec::Functions qw/ catfile rel2abs /;
use Digest::SHA qw/ sha256_hex /;
use Data::Dumper qw/ Dumper /;

my ( $root ) = @ARGV;

$root = rel2abs( $root );    # Allow for relative path input

my @file_data;

{
    opendir my $dh, $root or die qq{Cannot open directory "$root": $!};

    while ( readdir $dh ) {

        next if /^\./;

        my $file = catfile( $root, $_ );

        next unless -f $file;

        push @file_data, {
            path => $file,
            size => -s $file,
            id   => sha256_hex( $file ),
        };
    }
}

print Dumper \@file_data;

Upvotes: 2

Jonathan Dewein
Jonathan Dewein

Reputation: 984

Thanks to Wumpus Q. Wumbley's push suggestion, I have solved my problem:

my @array;
opendir DIR, $dir or die "cannot open dir $dir: $!";
while(my $file = readdir DIR) {
    next unless(-f "${\File::Spec->catfile($dir, $file)}");
    next if("$file" =~ m/^\./);
    #print "${\File::Spec->rel2abs($file)}\n";

    my  %hash = (
        path    =>  File::Spec->rel2abs($file),
        size    =>  -s $file,
        id      =>  sha256_hex($file),
    );

    push(@array, \%hash);

    #print Dumper sort \@array;
}
closedir DIR;

print Dumper \@array;

I create the "frame" for the hash, and then pass it to the array via reference and the push function.

Upvotes: 2

Cupcake Protocol
Cupcake Protocol

Reputation: 691

You have all the code, you are creating an array with the first element. Your way to dereference that hints at an easy way to add others:

my $i = 0;
while(my $file = readdir DIR) {
        next unless(-f "${\File::Spec->catfile($dir, $file)}");
        next if("$file" =~ m/^\./);
        print "${\File::Spec->rel2abs($file)}\n";
        $fileref[$i]{path} = File::Spec->rel2abs($file);
        $fileref[$i]{size} = -s $fileref[0]{path};
        $fileref[$i]{id} = sha256_hex(fileref[0]{path});
        $i++;
}

Upvotes: 0

Related Questions