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

Hi,
I am working on a perl script that needs to read in an entire text file and print out a certain section based on the input
Part of the text file is as follows--
Port 119 NNTP * Microsoft Ports-Microsoft Exchange supports a News server running at + this port. * RFC977 * RFC1036 #------------------------------------------- Port 120 CFDP (UDP) Coherent File Distribution Protocol (CFDP) * RFC1235 #------------------------------------------- Port 123 NTP (UDP) NTP (Network Time Protocol) * RFC2030 * RFC1129 #--------------------------------------------
I need the script to read in and search the text file for a line "Port $port" where $port is the port number and then print out everyline until the "#-----" line.

Currently I have the following which searches the text file and finds the line "Port $port"... But where do I go from there?

sub port_definition { my $port = @_[0]; open(FILE,"ipac_port_defs") || die_nice("Can not open file \"ipac_p +ort_defs\" for reading"); @port_file = <FILE>; close(FILE); foreach $line(@port_file){ if ($line =~ /^Port\s$port/){ $template{port_def} = $line; } } print "Content-type: text/html\n\n"; # Print headers parse("tpl/port_def"); }

If someone could just point me in the right direction.
Thanks,
Shawn

Replies are listed 'Best First'.
Re: Help Matching Sections of a file
by kvale (Monsignor) on Apr 05, 2004 at 00:10 UTC
    An alteration like this would do what you want:
    my $found_port; foreach my $line (@port_file) { if ($line =~ /^Port\s$port/) { $found_port = 1; $template{port_def} = $line; } next unless $found_port; last if $line =~ /^#----/; print $line; }

    -Mark

Re: Help Matching Sections of a file
by jmcnamara (Monsignor) on Apr 05, 2004 at 00:31 UTC

    Here is one way to do it. This example changes $/ to read the records one at a time. The regex may need to be extended a little and you could use an array instead of a hash.
    #!/use/bin/perl -w use strict; my %ports; $/ = "#-------------------------------------------\n"; while (<DATA>) { chomp; $ports{$1} = $_ if m/^\s+Port (\d+)/; } print $ports{120}; __DATA__ Port 119 NNTP * Microsoft Ports-Microsoft Exchange ... * RFC977 * RFC1036 #------------------------------------------- Port 120 CFDP (UDP) Coherent File Distribution Protocol (CFDP) * RFC1235 #------------------------------------------- Port 123 NTP (UDP) NTP (Network Time Protocol) * RFC2030 * RFC1129

    --
    John.

Re: Help Matching Sections of a file
by TilRMan (Friar) on Apr 05, 2004 at 02:21 UTC
    Gotta plug the .. operator (untested):
    my $port = "1234"; open FILE, blah blah blah or die; while (<FILE>) { print if /Port\s+$port/ .. /^#---/; } close FILE;
        Gotta plug the .. operator (untested):
      Nice one TilRMan! I was playing with it some more to get rid of the '#----' and I came up with:
      #!/usr/bin/perl my $port = $ARGV[0] or die "usage: $0 <portnumber>\n"; while (<DATA>) { print if /Port\s+$port/ .. /^#---/ and !/^#---/; } __DATA__ Port 119 NNTP * Microsoft Ports-Microsoft Exchange supports a News server running at + this port. * RFC977 * RFC1036 #------------------------------------------- Port 120 CFDP (UDP) Coherent File Distribution Protocol (CFDP) * RFC1235 #------------------------------------------- Port 123 NTP (UDP) NTP (Network Time Protocol) * RFC2030 * RFC1129 #--------------------------------------------
      Is there a way to do this with the '..' or '...' operator without having to do the extra match (the and !/^#---/ part)?
        How about (not well tested):
        while (<FOO>) { print if /Port\s+$port\b/ .. /^#---/ && last; }
        The last also fixes the (presumed) problem of looping through the whole file even after you've found the relevant section. Notice the \b too which I forgot.
Re: Help Matching Sections of a file
by BrowserUk (Patriarch) on Apr 05, 2004 at 01:19 UTC

    If your going to re-read the file every run, and assuming that the record separator is consistant (which it isn't in your sample data), then it might be more efficient to read the records, record by record by setting $/ to the separator, and only reading as many as needed.

    #! perl -slw use strict; sub port_definition{ my( $port, $def ) = shift; #open FILE, '<', .... local $/ = '#-------------------------------------------'; 1 until ($def = <DATA>) =~ m[Port $port]; return $def; } print port_definition 123 ; __DATA__ Port 119 NNTP * Microsoft Ports-Microsoft Exchange supports a News server running at + this port. * RFC977 * RFC1036 #------------------------------------------- Port 120 CFDP (UDP) Coherent File Distribution Protocol (CFDP) * RFC1235 #------------------------------------------- Port 123 NTP (UDP) NTP (Network Time Protocol) * RFC2030 * RFC1129 #-------------------------------------------

    Examine what is said, not who speaks.
    "Efficiency is intelligent laziness." -David Dunham
    "Think for yourself!" - Abigail
Re: Help Matching Sections of a file
by Elijah (Hermit) on Apr 05, 2004 at 01:14 UTC
    for (@port_file) { $match = 1 if (/^Port\s*$port/); undef $match if (/^#-+/); print if ($match); }
Re: Help Matching Sections of a file
by Happy-the-monk (Canon) on Apr 05, 2004 at 00:13 UTC

    to print $line

    print $line;

    you only want to print it if there was a match before, and if it isn't a #--------------------------------------------:

    if ( $line !~ m/^#\-{44}/ ) { undef $match } # kvale's solution with last
    # is probably better because it stops reading lines from the array.
    # Use this switch if you are going to print more than one section in the file.

    print $line if $match;

    You still have to switch on $match = 1 in the code you already have got.

    Cheers, Sören