0

I have the results of a program which gives me the results from some search giving me 2000+ file txt archives. I just need a specific line in each file, this is what I have been trying with Perl:

opendir(DIR, $dirname) or die "Could not open $dirname\n";

while ($filename = readdir(DIR)) {
 print "$filename\n";
 open ($filename, '<', $filename)or die("Could not open  file.");
  my $line;
  while( <$filename> ) {
    if( $. == $27 ) { 
    print "$line\n";
    last;
    }
 }
}
closedir(DIR);

But there is a problem with the $filename in line 5 and I don't know an alternative to it so I don't have to manually name each file.

0

1 Answer 1

2

Several issues with that code:

  • Using an old-school bareword identifier for the directory handle instead of a autovivified variable like you are for the file handle.

  • Using the same variable for the filename and file handle is pretty strange.

  • You don't check to see if the file is a directory or something else other than a plain file before trying to open it.

  • $27?

  • You never assign anything to that $line variable before printing it.

  • Unless $directory is your program's current working directory, you're running into an issue mentioned in the readdir documentation

    If you're planning to filetest the return values out of a readdir, you'd better prepend the directory in question. Otherwise, because we didn't chdir there, it would have been testing the wrong file.

    (Substitute open for filetest)

  • Always use strict; and use warnings;.


Personally, if you just want to print the 27th line of a large number of files, I'd turn to awk and find (Using its -exec test to avoid potential errors about the command line maximum length being hit):

find directory/ -maxdepth 1 -type -f -exec awk 'FNR == 27 { print FILENAME; print }' \{\} \+

If you're on a Windows system without standard unix tools like those installed, or it's part of a bigger program, a fixed up perl way:

#!/usr/bin/env perl
use strict;
use warnings;
use autodie;
use feature qw/say/;
use File::Spec;

my $directory = shift;
opendir(my $dh, $directory);
while (my $filename = readdir $dh) {
    my $fullname = File::Spec->catfile($directory, $filename); # Construct a full path to the file
    next unless -f $fullname; # Only look at regular files
    open my $fh, "<", $fullname;
    while (my $line = <$fh>) {
        if ($. == 27) {
            say $fullname;
            print $line;
            last;
        }
    }
    close $fh;
}
closedir $dh;

You might also consider using glob to get the filenames instead of opendir/readdir/closedir.

And if you have Path::Tiny available, a simpler version is:

#!/usr/bin/env perl
use strict;
use warnings;
use autodie;
use feature qw/say/;
use Path::Tiny;

my $directory = shift;
my $dir = path $directory;
for my $file ($dir->children) {
    next unless -f $file;
    my @lines = $file->lines({count => 27});
    if (@lines == 27) {
        say $file;
        print $lines[-1];
    }
}
Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.