In perl, I read in files from a directory, and I want to open them all simultaneously (but line by line) so that I can perform a function that uses all of their nth lines together (e.g. concatenation).
my $text = `ls | grep ".txt"`;
my @temps = split(/\n/,$text);
my @files;
for my $i (0..$#temps) {
my $file;
open($file,"<",$temps[$i]);
push(@files,$file);
}
my $concat;
for my $i (0..$#files) {
my @blah = <$files[$i]>;
$concat.=$blah;
}
print $concat;
I just a bunch of errors, use of uninitialized value, and GLOB(..) errors. So how can I make this work?
A lot of issues. Starting with call to "ls | grep" :)
Let's start with some code:
First, let's get list of files:
my @files = glob( '*.txt' );
But it would be better to test if the given name relates to file or directory:
my @files = grep { -f } glob( '*.txt' );
Now, let's open these files to read them:
my @fhs = map { open my $fh, '<', $_; $fh } @files;
But, we need a way to handle errors - in my opinion the best way is to add:
use autodie;
At the beginning of script (and installation of autodie, if you don't have it yet). Alternatively you can:
use Fatal qw( open );
Now, that we have it, let's get the first line (as you showed in your example) from all of the inputs, and concatenate it:
my $concatenated = '';
for my $fh ( @fhs ) {
my $line = <$fh>;
$concatenated .= $line;
}
Which is perfectly fine, and readable, but still can be shortened, while maintaining (in my opinion) readability, to:
my $concatenated = join '', map { scalar <$_> } @fhs;
Effect is the same - $concatenated contains first lines from all files.
So, whole program would look like this:
#!/usr/bin/perl
use strict;
use warnings;
use autodie;
# use Fatal qw( open ); # uncomment if you don't have autodie
my @files = grep { -f } glob( '*.txt' );
my @fhs = map { open my $fh, '<', $_; $fh } @files;
my $concatenated = join '', map { scalar <$_> } @fhs;
Now, it might be that you want to concatenate not just first lines, but all of them. In this situation, instead of $concatenated = ...
code, you'd need something like this:
my $concatenated = '';
while (my $fh = shift @fhs) {
my $line = <$fh>;
if ( defined $line ) {
push @fhs, $fh;
$concatenated .= $line;
} else {
close $fh;
}
}