Home > Back-end >  Using sed to add line above a set of lines
Using sed to add line above a set of lines

Time:02-01

I'm new to bash scripting, sorry if this has been answered elsewhere, couldn't find it in any searches I've done.

I'm using sed -i to add a line above an argument, for example.

for EFP in *.inp; do
^tsed -i "/^O */i FRAGNAME=H2ODFT" $EFP
done

and it works as expected. but I would like it to only add the line when the argument is true across multiple lines, like so:

O
C
O
C
FRAGNAME=H2ODFT
O
H
H
FRAGNAME=H2ODFT
O
H
H

Notice there's no added line above the two O's that are followed by C's.

I tried the following:

for FILE in *.inp; do
^tsed -i "/^O*\nH*\nH */i FRAGNAME=H2ODFT" $EFP
done

and I was expecting it to show up above the 3 lines that went O - H - H, but nothing happened, it passed through the file thinking that that argument was nowhere in the document.

I've looked elsewhere and thought of using awk, but I can't wrap my head around it.

Any help would be greatly appreciated! L

CodePudding user response:

I know you requested a sed solution, but, I have a solution based on awk.

  • We initialize the awk program with a stage which, overtime, will track the progress of "OHH"
  • If we receive another letter, we grow the stage until we get OHH, then, we print your required string and reset the stage
  • If we encounter a breakage, we print out whatever we accumulated in stage and reset stage
awk '
BEGIN { stage="" }
{
  if (stage == "" && $1 == "O") {
    stage="O"
  } else if (stage == "O" && $1 == "H") {
    stage="OH"
  } else if (stage == "OH" && $1 == "H") {
    print "FRAGNAME=H20DFT"
    print "O"
    print "H"
    print "H"
    print
    stage=""
  } else if (stage == "O") {
    print "O"
    print
    stage=""
  } else if (stage == "OH") {
    print "O"
    print "H"
    stage=""
  } else {
    print
  }
}
' < sample.txt

Where sample.txt contains:

O
C
O
C
O
H
H
O
H
H

CodePudding user response:

If input files aren't large to cause memory issues, you can slurp the entire file and then perform the substitution. For example:

perl -0777 -pe 's/^O\nH\nH\n/FRAGNAME=H2ODFT\n$&/gm' ip.txt

If this works for you, then you can add the -i option for inplace editing. The regex ^O*\nH*\nH * shown in the question isn't clear. ^O\nH\nH\n will match three lines having O, H and H exactly. Adjust as needed.

CodePudding user response:

This might work for you (GNU sed):

sed -Ei -e ':a;N;s/\n/&/2;Ta;/^O(\n.)\1$/i FRAGNAME=H2ODFT' -e 'P;D' file1 file2 

Open a 3 line window throughout the file and if the required pattern matches, insert the line of the desired text.

N.B. The \1 back reference matches the line before. Also the script is in two separate pieces because the i command requires to end in a newline which the -e option provides.

An alternative version of the same solution:

cat <<\! | sed -Ef - -i file{1..100}
:a
N
s/\n/&/2
Ta
/^O(\n.)\1$/i FRAGNAME=H2ODFT
P 
D
!
  • Related