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

I'm trying to insert a line after the last occurrence of a string in a file. Specifically, I want perl to:
  • find the last occurrence of 'echo' followed by anything, and ending in 'install_list',
  • insert another line of text right after it
    where example text to be parsed is as follows:

    echo ":os:5.7:" >> /mnt/install_list [text removed here ....] echo ":app:name-here:" >> /mnt/install_list
    The line I want to insert is:
    echo ":app:new-name-here:" >> /mnt/install_list
    I tried the following command as a start:
    perl -p -i.bak -e 's/echo*install_list(?!.*echo*install_list)/test/m' +test.cfg
    I expected 'test' to be inserted in the test.cfg file, but it didn't work. Thoughts on getting this to work?
  • -- Burvil

  • Replies are listed 'Best First'.
    Re: Look Ahead regexp to insert line?
    by choroba (Cardinal) on Oct 02, 2012 at 00:07 UTC
      This should work:
      #!/usr/bin/perl use warnings; use strict; open my $FH, '<', 'test.cfg' or die $!; my $pos; while (<$FH>) { $pos = $. if /echo.*install_list$/; # Remember the line numb +er. } die "Pattern not found.\n" unless $pos; $. = 0; # Reset the line number. seek $FH, 0, 0; # Start reading from the + beginning again. open my $OUT, '>', 'test.cfg.new' or die $!; while (<$FH>) { print {$OUT} $_; print {$OUT} "test\n" if $. == $pos; # After the remembered l +ine, print the extra text. } close $OUT; rename 'test.cfg.new', 'test.cfg';
      لսႽ† ᥲᥒ⚪⟊Ⴙᘓᖇ Ꮅᘓᖇ⎱ Ⴙᥲ𝇋ƙᘓᖇ
        Wouldn't using look ahead regular expressions be more efficient than going through the file letter by letter? That's what I was trying to do with the regexp in my post.

        -- Burvil

          My script goes through the file line by line, not letter by letter. It should work even for large files where stuffing everything into memory might be a problem.

          If you want to compare different solutions, benchmark. Benchmark can help you.

          لսႽ† ᥲᥒ⚪⟊Ⴙᘓᖇ Ꮅᘓᖇ⎱ Ⴙᥲ𝇋ƙᘓᖇ
    Re: Look Ahead regexp to insert line?
    by kcott (Archbishop) on Oct 02, 2012 at 01:51 UTC

      G'day bowei_99,

      Here's a solution using Tie::File you may find suitable.

      #!/usr/bin/env perl use strict; use warnings; use Tie::File; my $file = 'pm_file_insert_re.file'; my $re = qr{ \A echo .* install_list \z }x; my $insert_line = 'echo ":app:new-name-here:" >> /mnt/install_list'; tie my @lines, 'Tie::File', $file or die "Can't tie $file: $!"; my $last_found = 0; @lines = reverse map { /$re/ && ! $last_found++ ? ($insert_line, $_) : $_ } reverse @lines; untie @lines;

      Here's a run showing before and after file contents.

      ken@ganymede: ~/tmp $ cat pm_file_insert_re.file early text echo ":os:5.7:" >> /mnt/install_list middle text echo ":app:name-here:" >> /mnt/install_list later text ken@ganymede: ~/tmp $ pm_file_insert_re.pl ken@ganymede: ~/tmp $ cat pm_file_insert_re.file early text echo ":os:5.7:" >> /mnt/install_list middle text echo ":app:name-here:" >> /mnt/install_list echo ":app:new-name-here:" >> /mnt/install_list later text ken@ganymede: ~/tmp $

      -- Ken

    Re: Look Ahead regexp to insert line?
    by BillKSmith (Monsignor) on Oct 02, 2012 at 01:29 UTC

      If the file is not to large, it probably is easier to slurp the file into an array and then go through the array backwards looking for the first occurance.

      Update: I just heard of the module File::ReadBackwards. It may be what you need for longer files.

      Bill