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

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

Hello knowledgeable monks

I am seeking your wisdom on how to insert lines into a specific place in a stanza.

The stanza is as follows:

<stanza 12.34.56.78> sl1 this is my line sl2 justanotherline </stanza>

I would like to insert the code either after sl2 or just before the closing stanza tag. Either way is usable, whichever is simpler. (There could be more stanza lines after sl2. This is just an example.)

The code that I am using to locate the stanza that I want to change is as follows:

while (@array){ open ("VN", ">", "$array[0].tmp"); open(my $config, "<", $array[0]); my @change = <$config>; for (my $i=0; $i<=$#change; $i++){ if ($change[$i] =~ /\b$ip\b/){ $change[$i + 1] = $nc ."\n"; }

Thank you for your wisdom in this matter

j

Replies are listed 'Best First'.
Re: Insert lines into specific stanza line
by toolic (Bishop) on Aug 13, 2010 at 18:55 UTC
    Just detect the closing stanza tag, then add lines before it:
    use strict; use warnings; while (<DATA>) { print "extra line\n" if m{</stanza>}; print; } __DATA__ <stanza 12.34.56.78> sl1 this is my line sl2 justanotherline </stanza>

    prints out:

    <stanza 12.34.56.78> sl1 this is my line sl2 justanotherline extra line </stanza>
Re: Insert lines into specific stanza line
by oko1 (Deacon) on Aug 13, 2010 at 19:09 UTC
    while (@array)

    This will not do what you expect it to do (specifically, the loop will not terminate.) Please fix your code before posting it.

    Based on the rest of your code, I assume that your stanza is contained in a file, and that your data is laid out just the way you specify - tags, etc. each on a line of its own. In that case, the answer is fairly simple:

    #!/usr/bin/perl -w use strict; open my $config, '+<', 'stanza' or die "stanza: $!\n"; my @all = <$config>; seek $config, 0, 0; splice @all, -1, 0, "\tMy new data\n"; print $config @all; close $config;

    This will insert the "\tMy new data\n" string just before the closing stanza.

    Update: Re-read the OP's code, added another option for multiple stanzas in a file.

    In case you need to select one of multiple stanzas by IP, here's a different version:

    #!/usr/bin/perl -w use strict; open my $config, '+<', 'stanza' or die "stanza: $!\n"; my $ip = '12.34.56.78'; my $seen; while (<$config>){ if (m{^<stanza $ip>$}){ $seen = 1; } if ($seen && m{</stanza>}){ print "\tMy new data\n"; $seen = 0; } print; } close $config;

    --
    "Language shapes the way we think, and determines what we can think about."
    -- B. L. Whorf

      The second one is correct for what I am trying to accomplish. However I am getting the dreaded Use of uninitialized value in pattern match (m//)

      here is my code

      if ($change[$i] =~ /\b$ip\b/){ my $seen = 1; if ($seen && m{</stanza>}){ print $tl[0]; $seen = 0; }

      What am I missing?

        Please show enough code to establish context. I suspect that you're failing to define $ip (that's the only variable I see being used in a regex, and so the only place where you could have an uninitialized value in one), but since you've only showed this tiny snippet, there's no way for me to tell what else is wrong. There may also be other things wrong with your script, and I don't want to shoot in the dark.


        --
        "Language shapes the way we think, and determines what we can think about."
        -- B. L. Whorf
Re: Insert lines into specific stanza line
by jonadab (Parson) on Aug 13, 2010 at 19:17 UTC

    It took me three or four minutes to figure out what your for loop does, and I still have no idea what it has to do with the stated problem. I think I would try a different approach altogether...

    open INPUT, '<', 'inputfile'; open OUTPUT, '>', 'resultsfile'; local $/ = "</stanza>"; while (<INPUT>) { my $stanza = $_; if ($stanza =~ /<stanza/) { my ($dottedquad) = $stanza =~ /<stanza\s*(\d+[.]\d+[.]\d+[.]\d+)\s*>/; $stanza .= " sl3 yet another line\n"; # Make other changes to $stanza here as desired. print OUTPUT $stanza . $/; }}

      Unfortunately, your code is incorrect, and will produce the following output:

      <stanza 12.34.56.78> sl1 this is my line sl2 justanotherline </stanza> sl3 yet another line </stanza>

      Also, there's no newline at the last line of the above, so any subsequent additions will not be correctly formatted.


      --
      "Language shapes the way we think, and determines what we can think about."
      -- B. L. Whorf
Re: Insert lines into specific stanza line
by eff_i_g (Curate) on Aug 13, 2010 at 19:33 UTC
    I'm curious. What kind of format is this? Does it have a name or is it homegrown?
    <stanza 12.34.56.78> sl1 this is my line sl2 justanotherline </stanza>
Re: Insert lines into specific stanza line
by suhailck (Friar) on Aug 14, 2010 at 01:24 UTC
    A sed solution,

    sed  '/<\/stanza>/i\extra line1\nline2\nline3' filename

    OUTPUT
    <stanza 12.34.56.78> sl1 this is my line sl2 justanotherline extra line1 line2 line3 </stanza>


    ~suhail
Re: Insert lines into specific stanza line
by Marshall (Canon) on Aug 15, 2010 at 06:07 UTC
    Your problem statement is weak and it is hard for me to see how your code could work. But it could be that something like this is what you want.
    #!/usr/bin/perl -w use strict; my %add2stanza = ( '12.34.56.79' => "NEW DATA for 12.34.56.79", "12.34.56.80" => 'More new data for 12.34.56.80', ); while (<DATA>) { print; if (m/\<stanza\s+(.*)\>/) { my $number = $1; add_data($add2stanza{$number}) if $add2stanza{$number}; } } # not the best loop structure as the return is buried within # the loop but as a quick hack, shows some points about how # to do this... sub add_data { my $new_data = shift; while (<DATA>) { if (/\<\/stanza\>/) { print " $new_data\n"; print '</stanza>',"\n"; return; } else { print; } } } =prints <stanza 12.34.56.78> sl1 this is my line sl2 justanotherline </stanza> <line 1224> data </line> <stanza 12.34.56.79> sl1 this is my line sl2 justanotherline NEW DATA for 12.34.56.79 </stanza> <stanza 12.34.56.80> sl1 this is my line sl2 justanotherline More new data for 12.34.56.80 </stanza> <stanza 12.34.60.90> sl1 this is my line sl2 justanotherline </stanza> =cut __DATA__ <stanza 12.34.56.78> sl1 this is my line sl2 justanotherline </stanza> <line 1224> data </line> <stanza 12.34.56.79> sl1 this is my line sl2 justanotherline </stanza> <stanza 12.34.56.80> sl1 this is my line sl2 justanotherline </stanza> <stanza 12.34.60.90> sl1 this is my line sl2 justanotherline </stanza>