perl-diddler has asked for the wisdom of the Perl Monks concerning the following question:

I was trying to count lines in a file, AFAP.

Why didn't this work:

time perl -e '$_=[];{$/=\000,@$_=<>}; my $count=@$_;print $count, " li +nes\n";' </tmp/16 1 lines 0.47sec 0.28usr 0.18sys (99.77% cpu)
A correct solution (that takes the same time?)
time perl -e '{$/=undef, $_=<>} my $count=split /\000/,$_; print $coun +t, " lines\n";' </tmp/16 4118704 lines 0.47sec 0.28usr 0.18sys (99.77% cpu)
Shouldn't the first have read it into a temp array pointed to by $_, then got the count from @$_?

FWIW, also tried binmode STDIN at the front - same thing. What am I forgetting?

Replies are listed 'Best First'.
Re: Why doesn't this work?
by LanX (Saint) on Apr 12, 2013 at 21:42 UTC
    try $/="\0"

    unquoted \0 is a reference to a literal number (i.e. read-only scalar) ...

    DB<135> $/=\0 => \0 DB<136> ref $/ => "SCALAR" DB<137> ${$/} => 0

    ... and not the escape sequence for NUL byte!

    DB<146> $/="\0" => "\0" DB<147> ord $/ => 0

    Cheers Rolf

    ( addicted to the Perl Programming Language)

      *Bingo*. That was it! *doh!* <palmslaps self>
        maybe shorter?

        DB<178> open $fh,"<",\"1\0 2\0 3" DB<179> print scalar do {local $/="\0"; () = <$fh> } 3

        update
        as one liner
        perl -e '$/="\0"; print $a=()=<>'

        Cheers Rolf

        ( addicted to the Perl Programming Language)

Re: Why doesn't this work?
by ww (Archbishop) on Apr 12, 2013 at 21:59 UTC
    You might want to read about $/ in perldoc perlvar. It would also be well to recognize that settng "$/=\000" is far from the same thing as $/=undef or $/="\000".

    Not an issue in your one-liners, but you should probably use any modified $/ ( or local $/=... ) in as narrow a scope as possible when you change it, or make sure you reset it in any other instance (other'n your one-liners).


    If you didn't program your executable by toggling in binary, it wasn't really programming!

      I forgot the 'my' in the braces, but know the point you were making -- i.e. that's why it was in braces.

      I wasn't trying to use slurp mode & $/=undef in the one example, -- was trying to recognize "\000" (NUL) as the line delimiter (in this case a list of NUL-terminated filenames) and use that to read the whole thing into an array and then seeing how many array members I had.

        Make that 'my' a 'local'... geez... trying to respond too fast. Just to be sure I checked my code and only find one place where I used slurp mode that wasn't in a 1-liner, and I used local there. Glad you reminded me -- as I'd started to get it in my head that braces or my were needed...

        Looks like most common usage for me is shell-1-liners where Shell can't deal with NUL terminated lines, so need perl to do that.

Re: Why doesn't this work?
by Corion (Patriarch) on Apr 12, 2013 at 21:35 UTC

    For me, your first example works:

    C:\Dokumente und Einstellungen\corion>perl -MData::Dumper -e "{$_=[];l +ocal $/=qq(\n); @$_=<>};warn Dumper $_; my $count=@$_; print $count, +' lines';" \boot.ini $VAR1 = [ '[boot loader] ', ... ]; 5 lines

    while the second one does always output "1 lines". This is the special behaviour of split. To make split produce more results than needed on the left hand side, you need to give it a LIMIT. Also, you will want to call split in list context using this operator =()=:

    > perl -e "{$/=undef, $_=<>} my $count=()=split /\n/,$_,-1; print $cou +nt" \boot.ini 6
      Huh?... but the 2nd one worked. What did you do to your perl that it doesn't? I'm using this split:
      Splits the string EXPR into a list of strings and returns the list in list context, or the size of the list in scalar context. Size of list in scalar context. && re: limit: If LIMIT is omitted (or, equivalently, zero), then it i +s usually treated as if it were instead negative but with + the exception that trailing empty fields are stripped (empt +y leading fields are always preserved);
      What version of split are you using?

        Ah, sorry - I don't know what I did wrong while testing this. The documentation is correct and neither the limit nor the forced list context are needed:

        > perl -e "{$/=undef, $_=<>} my $count=split /\n/; print $count" \boot +.ini

        works just as well, and just as documented.

Re: Why doesn't this work?
by hdb (Monsignor) on Apr 12, 2013 at 21:33 UTC

    This works for me:

    time perl -e '$_=[];@$_=<>; my $count=@$_;print $count, " lines\n";' < + somefile.txt time perl -e '{$/=undef, $_=<>} my $count=split /\n/,$_; print $count, + " lines\n";' < somefile.txt

    Your second version does not when splitting on \000.

      The examples you list don't split on \000.

      They are not the examples from my code. (i.e. 2nd example was..)

      time perl -e '{$/=undef, $_=<>} my $count=split /\000/,$_; print $coun +t, " lines\n";' </tmp/16
      Wasn't it? Or am I REALLY confused?

        I cound not find even a single test file that would be more than one line under splitting on \000 so I was resorting to replacing the delimiter. So what I stated was correct but possibly irrelevant... ;)