Open a specific directory and read all the text files

Below codes is get into a folder($directory) search for text file and print out the filename and size. It is working fine if CMD path is c:\modules\STP\New, it able to read the .txt files in the specific folder. I guess it is working fine due to it matched the $directory. Unfortunately, when I run the codes on different path such as c:\modules\STP, it shows error. The error is Can't call method "size" on a undefined value at line 12. How can I fix this issue?

my $directory = 'c:\modules\STP\New';
my $file='filename.txt';
my $OUTFILE;

open $OUTFILE, '>>', $file;

my @files = do {
    opendir (my $dh, $directory);
    grep {/^.*\.txt\z/si} readdir($dh);
};
foreach(@files){
 my $filesize = stat($_)->size;   #<<<< LINE 12

  print { $OUTFILE } "$filesize $_" ,"\n";

}

Expected Result:

I expected the codes can be run in c:\modules\STP or any other CMD path. I doing this because I wish to reuse the code. Soon the $directory will not be hardcoded. Any solution or useful resource to share? Thanks!

Upvotes: 1

Views: 2352

Answers (4)

Nicholas Anderson
Nicholas Anderson

Reputation: 593

#!/usr/bin/perl

use strict;
use warnings;
use File::Find;

my @content;
my $basedir = $ARGV[0];

if ( ! $basedir ){ $basedir = '/home/iwts/scripts'; }

find( \&wanted, $basedir );
foreach my $file ( @content ) {
    my $cmd = `ls -lh $file | awk '{print \$9 "\t" \$5 }'`;
    print $cmd;
}

sub wanted() { if ( $_ =~ m|^.*\.txt| ) { push @content, $File::Find::name; return; } }

Upvotes: 0

Miller
Miller

Reputation: 35198

You simply need to include full path information when doing a file operation like stat.

The following is a cleanup of your script:

use strict;
use warnings;
use autodie;

use File::stat;

my $directory = 'c:\modules\STP\New';
my $file = 'filename.txt';

open my $outfh, '>>', $file;

opendir my $dh, "$directory";

while (my $file = readdir $dh) {
    next unless $file =~ /\.txt$/i;
    my $filesize = stat("$directory\\$file")->size;   # Include full path info
    print $outfh "$filesize $file\n";
}

Enhancement by using Path::Class

To ease the managing of full path and file info, I would recommend using a module like Path::Class.

The following does the same thing as your script, but ensures you aren't missing path information in your file test:

use strict;
use warnings;
use autodie;

use Path::Class;

my $dir = dir('c:\modules\STP\New');

open my $outfh, '>>', 'filename.txt';

while (my $file = $dir->next) {
    next unless $file =~ /\.txt$/i;
    printf $outfh "%s %s\n", $file->stat->size, $file->basename;
}

Upvotes: 2

clt60
clt60

Reputation: 63902

You can also use some CPAN modules, what allows you many additional easying functions, my favorite is Path::Tiny.

From the docs:

This module provide a small, fast utility for working with file paths. It is friendlier to use than File::Spec and provides easy access to functions from several other core file handling modules. It aims to be smaller and faster than many alternatives on CPAN while helping people do many common things in consistent and less error-prone ways.

Your code with Path::Tiny

use 5.010;
use warnings;
use Path::Tiny;

my $directory = $ARGV[0] // ".";        #use 1st argument or current dir as default
my $pattern = qr/\.txt$/i;              #define your pattern to match

$iter = path($directory)->iterator({ recurse => 0 });
                                     #change to 1, if want recursively search subdirs

while ( $path = $iter->() ) {
    next unless $path =~ $pattern;
    say "$path: ", $path->stat->size;
}

Upvotes: 0

mpapec
mpapec

Reputation: 50637

Use glob() which already gives full path to files, or add path using map,

my @files = do {
  opendir (my $dh, $directory) or die $!;
  map { "$directory/$_" }
  grep { /^.*\.txt\z/si }
  readdir($dh);
};

Upvotes: 2

Related Questions