Beefy Boxes and Bandwidth Generously Provided by pair Networks
go ahead... be a heretic
 
PerlMonks  

Printing specific line numbers

by tsk1979 (Scribe)
on Jun 01, 2006 at 18:11 UTC ( [id://553109]=perlquestion: print w/replies, xml ) Need Help??

tsk1979 has asked for the wisdom of the Perl Monks concerning the following question:

Right now I have a script which reads in two files, first file containing a list of line numbers, and second file is a text file. This script prints the lines and 2 following lines as specified by the line number. For example the line number script can be
44 87 345
This script will display line numbers 44,45,46 87,88,89 and 345 346 347. I was looking for a way to do this without using the perl script, ie just using a single command line. Any leads?

Replies are listed 'Best First'.
Re: Printing specific line numbers
by ikegami (Patriarch) on Jun 01, 2006 at 18:31 UTC
    my @line_nums = (44, 87, 345); my $line_num = shift(@line_nums); my $more = 2; while (<>) { next if $. != $line_num; print; if ($more) { --$more; ++$line_num; shift(@line_nums) if @line_nums && $line_nums[0] == $line_num; next; } last unless @line_nums; $line_num = shift(@line_nums); $more = 2; }

    Tested:

    • Handles overlap. For example, asking for lines 1, 2 & 4 prints lines 1, 2, 3, 4, 5 & 6.
    • Handles end of file. For example, asking for line 15 of a file with 16 lines prints lines 15 and 16 (and doesn't give an error for the missing line 17).

    Features:

    • Efficient. Only looks at one element of @line_nums per line of the input file.
    • Efficient. Stops reading the input file if there are no more lines to print.

    Limitations:

    • Assumes @line_nums is sorted. This assumption is easy to remove.

    To do:

    • Read the line numbers one at a time, instead of having them all in memory.
Re: Printing specific line numbers
by davido (Cardinal) on Jun 01, 2006 at 19:57 UTC

    The following method stores the lines you wish to print in a hash as an action tabletm. This allows for a fairly simple approach to reading the second text file, and deciding line by line whether to print or not. This method is fairly efficient:

    use strict; use warnings; my %find; open INDEX, '<', shift( @ARGV ) or die $!; while( <INDEX> ) { @find{ $_ .. $_ + 2 } = (); } close INDEX; while( <> ) { next unless exists $find{$.}; print $_; delete $find{$.}; last unless keys %find; }

    Advantages:

    • The script won't error-out if you request an index beyond the end of the text file.
    • Overlap is fine. Requesting lines 1, 2, and 4 will print 1, 2, 3, 4, 5, 6, not 1, 2, 2, 3, 4, 4, 5, 6.
    • There's no need to sort the line index list.
    • Terminates the search once there are no more indices in the list.

    The above code is "one liner friendly", and can be expressed like this:

    perl -ne "BEGIN{open I, '<', shift(@ARGV); while(<I>){ @find{$_ .. $_+ +2}=();}} next unless exists $find{$.}; print; delete $find{$.} last u +nless keys %find;"

    Dave

Re: Printing specific line numbers
by davidrw (Prior) on Jun 01, 2006 at 19:12 UTC
    you can use Tie::File and treat the file as an array, and take an array slice:
    use Tie::File; use Fcntl 'O_RDONLY'; my @array; my $filename = '/etc/passwd'; tie @array, 'Tie::File', $filename, mode=>O_RDONLY or die; open NUMS, "line_num_file" or die; my @line_nums = map { s/\s+$//s; $_ } <NUMS>; close NUMS; my @lines = @array[ @line_numes ];
      And as requested, here's a command-line version of that:
      perl -MTie::File -ne 'BEGIN{tie(@f,"Tie::File",shift,mode=>0,autochomp +=>0) or die "tie failed $!"} print@f[$_..$_+2]'

      Give the filename to read first, and any other filenames given will contain line numbers; if only one filename is given, numbers come from STDIN. For example:

      $ echo 0 |perl -MTie::File -ne 'BEGIN{tie(@f,"Tie::File",shift,mode=>0 +,autochomp=>0) or die "tie failed $!"} print@f[$_..$_+2]' /etc/passwd root:x:0:0:root:/root:/bin/bash bin:x:1:1:bin:/bin:/sbin/nologin daemon:x:2:2:daemon:/sbin:/sbin/nologin $
Re: Printing specific line numbers
by GrandFather (Saint) on Jun 01, 2006 at 18:22 UTC

    Use $. to determine the current file line number. I don't do command line, but the following should point you in the right direction:

    use strict; use warnings; my $tempFileName = 'temp.txt'; open tempFile, '>', $tempFileName; print tempFile <<TEMP; Line one Line two Line three Line four Line five TEMP close tempFile; open tempFile, '<', $tempFileName; while (<tempFile>) { print "$_" if $. >= 2 && $. <= 4; } close tempFile;

    Prints:

    Line two Line three Line four

    DWIM is Perl's answer to Gödel
Re: Printing specific line numbers
by socketdave (Curate) on Jun 01, 2006 at 18:18 UTC
    If you have head and tail:
    head -n 46 filename|tail -n 3 head -n 89 filename|tail -n 3 head -n 347 filename|tail -n 3
Re: Printing specific line numbers
by Anonymous Monk on Jun 01, 2006 at 23:23 UTC
    Come on, someone needs to golf it better than that!
    perl -e '$cond = join " || ", map { "($_ .. \$. == $_ + 2)" } do { loc +al @ARGV = shift; <> }; eval qq{ while(<>) { print if $cond } };' lin +es test.txt
    Which, when the whitespace is fully condensed, leaves you with:
    perl -e'$cond=join"||",map{"($_..\$.==$_+2)"}do{local@ARGV=shift;<>};e +val"($cond)&&print while<>"' lines test.txt

Log In?
Username:
Password:

What's my password?
Create A New User
Domain Nodelet?
Node Status?
node history
Node Type: perlquestion [id://553109]
Approved by planetscape
help
Chatterbox?
and the web crawler heard nothing...

How do I use this?Last hourOther CB clients
Other Users?
Others chilling in the Monastery: (3)
As of 2024-04-23 23:10 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found