Counting number of lines with conditions

Viewed 265

This is my script count.pl, I am trying to count the number of lines in a file. The script's code :

chdir $filepath;

if (-e "$filepath"){
$total = `wc -l < file.list`;
printf "there are $total number of lines in file.list";
}

i can get a correct output, but i do not want to count blank lines and anything in the file that start with #. any idea ?

4 Answers
perl -ne '$n++ unless /^$|^#/ or eof; print "$n\n" if eof'

Works with multiple files too.

 perl -ne '$n++ unless /^$|^#/ or eof; END {print "$n\n"}'

Better for a single file.

open(my $fh, '<', $filename);
my $n = 0;
for(<$fh>) { $n++ unless /^$|^#/}
print $n;

As this is a Perl program already open the file and read it, filtering out lines that don't count with

open my $fh, '<', $filename or die "Can't open $filename: $!";
my $num_lines = grep { not /^$|^\s*#/ } <$fh>; 

where $filename is "file.list." If by "blank lines" you mean also lines with spaces only then chagne regex to /^\s*$|^\s*#/. See grep, and perlretut for regex used in its condition.

That filehandle $fh gets closed when the control exits the current scope, or add close $fh; after the file isn't needed for processing any more. Or, wrap it in a block with do

my $num_lines = do { 
    open my $fh, '<', $filename or die "Can't open $filename: $!";
    grep { not /^$|^\s*#/ } <$fh>; 
};

This makes sense doing if the sole purpose of opening that file is counting lines.

Another thing though: an operation like chdir should always be checked, and then there is no need for the race-sensitive if (-e $filepath) either. Altogether

# Perhaps save the old cwd first so to be able to return to it later
#my $old_cwd = Cwd::cwd;
chdir $filepath or die "Can't chdir to $filepath: $!";

open my $fh, '<', $filename or die "Can't open $filename: $!";
my $num_lines = grep { not /^$|^\s*#/ } <$fh>; 

A couple of other notes:

  • There is no reason for printf. For all normal prints use say, for which you need use feature qw(say); at the beginning of the program. See feature pragma

  • Just in case, allow me to add: every program must have at the beginning

    use warnings;
    use strict;
    

Perhaps the original intent of the code in the question is to allow a program to try a non-existing location, and not die? In any case, one way to keep the -e test, as asked for

#my $old_cwd = Cwd::cwd;
chdir $filepath or warn  "Can't chdir to $filepath: $!";

my $num_lines;
if (-e $filepath) { 
    open my $fh, '<', $filename or die "Can't open $filename: $!";
    $num_lines = grep { not /^$|^\s*#/ } <$fh>; 
}

where I still added a warning if chdir fails. Remove that if you really don't want it. I also added a declaration of the variable that is assigned the number of lines, with my $total_lines;. If it is declared earlier in your real code then of course remove that line here.

Using sed to filter out the "unwanted" lines in a single file:

sed '/^\s*#/d;/^\s*$/d' infile | wc -l

Obviously, you can also replace infile with a list of files.

The solution is very simple, no any magic.

use strict;
use warnings;
use feature 'say';

my $count = 0;

while( <> ) {
    $count++ unless /^\s*$|^\s*#/;
}

say "Total $count lines";

Reference: <>

Related