Print line that meets conditions in different lines with awk

Viewed 83

total newbie. It is possible with awk to print the following:

w171930 t1 Y z2545377 <--- print this line
w171930 t2 X z4495648
w171931 t1 Y z2555698 <--- print this line
w171931 t2 X z5505690
w171932 t1 Y z2554345 <--- print this line
w171932 t2 X z5507345
w171933 t1 Y z2214694
w171933 t2 Y z8022710
w171933 t3 Y z2143462
w171933 t4 Y z6217556
w171933 t5 Y z9608343
w171933 t6 Y z9984446
w171933 t7 Y z2985572
w171933 t8 Y z6334512 <--- print this line
w171933 t9 X z6503375
w171943 t1 Y z2441603 <--- NO print this line
w171943 t2 X z4644534
w171943 t3 Y z2164440
w171944 t1 Y z2165532

Search on $3 == "X" and print the previous line if the next line has a $2 == "t1"

Goal

w171930 t1 Y z2545377
w171931 t1 Y z2555698
w171932 t1 Y z2554345
w171933 t8 Y z6334512

I have only managed to print the previous line but I don't know how to perform the complete condition

awk '$3 == "Y" { Y=$0; next; } { if ($3 =="X") print Y;}'

I'm sorry if I don't express myself correctly

enter image description here

5 Answers

Another solution with awk:

$ awk '$2=="t1" && third=="X"{print line2}
       {line2=line1; line1=$0; third=$3}' ip.txt
w171930 t1 Y z2545377
w171931 t1 Y z2555698
w171932 t1 Y z2554345
w171933 t8 Y z6334512
  • line1 will have the previous line and line2 will have previous but one line
  • third saves the 3rd field content of the previous line
  • when the current line's 2nd field is t1, check if previous line's 3rd field is X and if so, print previous but one line

Regarding your requirement:

Search on $3 == "X" and print the previous line if the next line has a $2 == "t1"

As in life, in software it's much easier to do things based on what HAS happened rather than what WILL happen, so don't write requirements based on what the NEXT thing will be, write them based on what the PREVIOUS things were and you'll find it much easier to conceptualize and write the code to implement them. In this case your requirement should be written as:

If $2 == "t1" in the current line and $3 == "X" in the previous line then print the line before that.

which leads to the obvious implementation:

$ awk '($2=="t1") && (p3=="X") {print pp0} {pp0=p0; p0=$0; p3=$3}' file
w171930 t1 Y z2545377 <--- print this line
w171931 t1 Y z2555698 <--- print this line
w171932 t1 Y z2554345 <--- print this line
w171933 t8 Y z6334512 <--- print this line
awk '$2=="t1"{ if(prev2!="" && prev!="") print prev2 }
     { prev=($3=="X"?prev2:""); prev2=$0 }' input
  • $2=="t1" only for lines where the second element = "t1". When prev and prev2 have a value, then we should print the value.
  • prev will contain the value of prev2 when the third element is equal to "X"
  • prev2 will have the value of the line which needs to be returned.

You can do this with a regex if desired which can be done in Perl:

perl -0777 -nE 'while (/^.*\R(?=(?:\S+\s){2}X\s\S+\R\S+\st1)/gm) {print $&}' file

Or, with slight modification to the regex, you can use GNU Grep:

grep -ozP '.*\R(?=(?:\S+\s){2}X\s\S+\R\S+\st1)' file | tr -d '\000'

Or ruby:

ruby -e 'puts $<.read.scan(/(.*\R)(?=(?:\S+\s){2}X\s\S+\R\S+\st1)/)' file

Any of those (with your example input) prints:

w171930 t1 Y z2545377 <--- print this line
w171931 t1 Y z2555698 <--- print this line
w171932 t1 Y z2554345 <--- print this line
w171933 t8 Y z6334512 <--- print this line

slightly verbose, but it gets the job done :

<<<"${aa}" mawk '

 BEGIN {
       _+=++_
 } {
     do {____=$((__=$_)<"") 

     } while($(_+getline)=="Y")
   
     if(___<("X"==$++_)) {

       printf("%s%.*s",____,\
           (___=__!="t1")<_,ORS) }; -—_}'

w171930 t1 Y z2545377
w171931 t1 Y z2555698
w171932 t1 Y z2554345
w171933 t8 Y z6334512
Related