[Perl] Insert lines before lines.

Hi,

New problem, or challenge as they prefer in the US.

I need to insert some lines in a file before certain other lines.
To make it more clear:

Original file:

aaaa
bbbbb
ccccc
ddddd
bbbbb
fffff
ggggg

Now I want to insert the line "NEW_NEW_NEW" when I match "fffff", but I want it to be before the 5th line with "bbbbb".
Like this:

aaaa
bbbbb
ccccc
ddddd
NEW_NEW_NEW
bbbbb
fffff
ggggg

Notice that the line with "bbbbb" is more times present, so I cannot use that as search string.
It is the last present line with "bbbbb" though, perhaps that helps.

I tried:

$file_to_update = "test.txt";
open (FILE_TO_UPDATE, "<$file_to_update");
my @lines = <FILE_TO_UPDATE>;
close (FILE_TO_UPDATE);

open (FILE_TO_UPDATE, ">$file_to_update");
for (@lines) {
  if ($_ =~ m/fffff/) {
  print FILE_TO_UPDATE "NEW_NEW_NEW";
  }
  print FILE_TO_UPDATE;
}
close (FILE_TO_UPDATE);

But then I get the wrong order:

aaaa
bbbbb
ccccc
ddddd
bbbbb
NEW_NEW_NEW
fffff
ggggg

Anyone with some ideas ?

E.J.

perl -0pe'  
  s/bbbbb(?!.*bbbbb)/NEW_NEW_NEW\n$&/s
  ' infile

Thanks for the one-liner.
For this example it works, but you understand it is part of a bigger universe :slight_smile:
And I am having trouble incorporating it in the example code.
Not to mention that I do not understand the wizardry in your one-liner.
I understand it is a conditional substitute, but what is the $ and the & doing for example ?

But let me make the problem a bit more real life.

Here is the test.txt data file (edited for the forum).
It is a ksh script and this is how it comes after an installation.
I need to modify it and I would like to have this modification done by a script.

if [ -f $topload_inprogress ]
then
		blahlblah
		rm $inprogress
		exit 1
fi 

if [ ! -f $params ]
then
	blahlblah
	rm $inprogress
	exit 1
fi 

if(($?==1))
then 
	blahlblah
	rm $inprogress
	exit 1
fi

if [[ -f $ftp_fin ]]
then
	rm $ftp_fin
else
	blahlblah
	rm $inprogress
	exit 1
fi

files=`ls $CADATA/srcdata/$source/custom/BULK/after`
for name in $files
do
	blahlblah
done

rm $inprogress
$CAROOT/prog/ca_log_message TOPOLOGY_STAT "Finished the bulk ftp topology load of $source data source."
exit 0

I need to insert a block-text (6 lines) between last 'done' and the 'rm $inprogress'.

Can your "(?!.*bbbbb)" be of any help there ?

Greetings,

E.J.

Can you use the pattern ^rm $inprogress (at the beginning of the line) as a marker?
Or perhaps its last occurrence?
Do you really need both patterns done .. rm $inprogress?
Why Perl? I suppose the task could be done with a lighter tool (e.g. sed).

The file comes like this, I have no control over it.
And if I have to "prepair" it manually, then I can make the text insert manually too.
But I don't want to.

The last "rm $inprogress" is at the end of the file, so it is the last occurrence.

The text-block needs to be between the last "done" and the last "rm $inprogress", that's a given.

I use Perl, because I need to make many more modifications in other files and I need to start things, stop things, etc.
Perl is perfect for this.
This is one of the many sub-routines in the script.
In fact I need to make 2 text-block insertions in this file and the second one has this problem I described.

If necessary I could call a sed command from within Perl, right ?
But I do not see why it could not be done with Perl.
Okay, I can't, but I am not the brightest Perl programmer around :slight_smile:
Not yet. :wink:

Now I am thinking about counting the "done"s, printing all lines including the last "done". Then print my text-block and then print the last lines.
Or perhaps read 2 lines, test the 2nd line for "Finished the bulk ftp".
Print all lines until the test is true, print my test-block and print the last lines.
Something should be possible.

E.J.

OK,
you can use something like this (you should add exception handling, of course):

#!/usr/bin/perl

use warnings;
use strict;

my ( $shellscript, $pattern ) = ( shift, 'rm \$inprogress' );

my $newcode = <<_NEWCODE_;
line1
line2
...
line6
_NEWCODE_

{
    local ( $^I, @ARGV, $/ ) = ( '', $shellscript );
    while (<>) {
        s/$pattern(?!.*$pattern)/$newcode\n$&/s;
        print;
    }
}

You don't need to call sed from Perl :slight_smile:
I'm not saying that you should not use Perl for this,
but that sed is sufficient for this task.
And yes, of course, if this is only one of the tasks and you have other code in Perl, you should do it all in Perl.

Hi,

Thanks for hanging in there.
I tried your code and get this:

C:\Documents and Settings\evroom\Desktop>update.pl test.txt
Can't do inplace edit without backup at C:\Documents and Settings\evroom\Desktop\update.pl line 17.

I tried using Perl on Windows (eventually it will run on a Unix machine), perhaps it's too picky.

When using this part in my code, I get the same output as the input:

s/$pattern(?!.*$pattern)/$newcode\n$&/s;

E.J.

-----Post Update-----

This works for me:

#!/usr/bin/perl -w #-d

use warnings;
use strict;

my @lines;
my $counter1 = 0;
my $counter2 = 0;

my $newcode = "line1
line2
line3
line4
line5
line6\n
";

open ( TEXT, "<test.txt");
  @lines = <TEXT>;
close TEXT;

for (@lines) {    
  if ( $_ =~ m/rm \$inprogress/ ) { $counter1++; }
}

open ( TEXT, ">test.txt");
for (@lines) {   
  if ( $_ =~ m/rm \$inprogress/ ) { $counter2++; }
  if ( $counter2 == $counter1) { print TEXT $newcode; $counter1 = 0;}
print TEXT;
}
close TEXT;

It will not win the beauty-contest but it fits the Perl slogan "There is more than one way to do it".

E.J.

I'm glad you found a solution yourself.