http://www.perlmonks.org?node_id=1041033

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

Hi monks,

I have a problem doing something the smart way. I am new to programming in perl so am unsure what the best solution is.

My problem is this: I have a text file that has many lines. I want to go through it to match lines with /^hostname / and once I find a line that contains this, I want to go through the following lines, do some actions on said lines until I find another line that contains /^hostname / and repeat.

Here is what I did so far:

sub hostname{ print $fd $_; while (<$fh>){ if ($_ =~ /^hostname /){ hostname; }else{ # print $_; $tot2++; } } $tot++; } while(<$fh>){ hostname if ($_ =~ /^hostname /); }

Obviously I open and close the files and declare the necessary variables. However, although this works, it gives me a deep recursion warning so I am fairly certain there is a better way to do this that I just haven't thought of.

Thanks for your help!

jrvd

Replies are listed 'Best First'.
Re: Too much recursion
by blue_cowdawg (Monsignor) on Jun 27, 2013 at 15:09 UTC
        However, although this works, it gives me a deep recursion

    I'm not surprised. The question this begs to me is why the recursion. Something like this should suffice:

    | hand waving here. while(<$fd>){ next if $_ =~ m/^hostname /; print $_; $tot2++; } $tot++
    Now, what is troubling me is when or how your re initializing the vars $tot2 and $tot. That would change my looking logic ever so slightly if I knew that.


    Peter L. Berghold -- Unix Professional
    Peter -at- Berghold -dot- Net; AOL IM redcowdawg Yahoo IM: blue_cowdawg

      Hi,

      Thanks for your help. I have put my entire code so that you may see where my variables were initialized.

      #!/usr/bin/perl use strict; use warnings; my $fileName = "version.txt"; my $fileDest = "version_modified.txt"; die "Problème d'ouverture du fichier $fileName" unless open(my $fh, '< +', $fileName); die "Problème de création du fichier $fileDest" unless open(my $fd, '> +', $fileDest); my $tot = 0; my $tot2 = 0; sub hostname; sub hostname{ print $fd $_; while (<$fh>){ if ($_ =~ /^hostname /){ hostname; }else{ # print $_; $tot2++; } } $tot++; } while(<$fh>){ hostname if ($_ =~ /^hostname /); } print $tot."\n"; print $tot2."\n"; close $fh; close $fd;

      I am not sure if the code you suggested should only replace a certain part. I might not have correctly expressed what I was looking for however. What I need to do is find the line containing "hostname ", print that, then do some various modifications on the following lines until I find another "hostname " and repeat. Right now, all I was doing was incrementing $tot2 until I got this to work but eventually this will change.

      Thanks!

      jrvd

        Seems like you can do the same without a recursion:

        Update: Add "print $fd $_" to preserve original behavior.

        sub hostname{ print $fd $_; while (<$fh>){ if ($_ =~ /^hostname /){ print $fd $_; $tot++; next; }else{ $tot2++; } } $tot++; # keep initial increment }
Re: Too much recursion
by pklausner (Scribe) on Jun 27, 2013 at 16:11 UTC
    No recursion needed here. This is simple awk-style stream processing like in the (untested!) sample. Re-arrange the sequence of the matching rules to include/exclude the leading/ending patterns for your hostname sections.
    while (<>) { /^hostname/ and do { $host_section = 1; ++$tot; # number of hostnames }; if ($host_section) { print $_; ++$tot2; # lines in host sections # do some processing... } /^end-of-hostname-pattern/ and do { $host_section = 0; }; }

      In your code, it seems to me that you would wish to insert a next statement after the line with “number of hostnames” comment, so that the processing of that particular record ends at that point instead of falling-through.

      The general structure of an awk program (this being one of the original inspirations for Perl) is instructive:   the awk language consists of regular-expression patterns followed by code-sections that are executed if a particular pattern is matched.   (Other special sections are executed at the beginning and at the end of the file.)   This encourages one to approach problems like these by first identifying each “type of line” that the file might contain ... building a regular expression for each ... then deciding what to do with each one.   In this example, there are at least two types:   “is a hostname line,” and “isn’t.”

      Recursion isn’t needed to solve problems like these.   For dealing with very complicated inputs, the notion of a Finite-State Machine (FSM) can be useful.

Re: Too much recursion
by Dallaylaen (Chaplain) on Jun 27, 2013 at 15:53 UTC

    It looks like you are reading some kind of configuration file. I would suggest using a primitive state machine, something like:

    my $host; while (<$fh>) { if (/^hostname/) { $host = new_host( $_ ); next; }; if ($host) { add_to_host( $host, $_ ); }; }; sub new_host { ... }; # return id of new host section sub add_to_host { ... }; # do whatever is needed

    I don't know what the file format is, and what the code really should do, so I can't say what's in the two last subs. At least there's no recursion, and you can process as many hostnames as you want.