Grep text vertically

Viewed 212

I have a text file as like this

scaffold20  6146680 .   T   C   44.4146 .   DP=2;VDB=0.02;SGB=-0.4
scaffold20  6146696 .   G   A   8.13869 .   DP=1;SGB=-0.379885;MQ0
scaffold20  6146760 .   A   G   8.13869 .   DP=1;SGB=-0.379885;MQ0
scaffold20  6146785 .   A   G   8.13869 .   DP=1;SGB=-0.379885;MQ0
scaffold20  6146864 .   A   C   153 .   DP=7;VDB=0.637622;SGB
scaffold20  6146867 .   G   A   11.4845 .   DP=8;VDB=0.82;SGB=-0.45
scaffold20  6146914 .   G   A   20.2676 .   DP=5;VDB=0.06;SGB=-0.45
scaffold20  6147094 .   G   A   44.4146 .   DP=2;VDB=0.44;SGB=-0.45
scaffold20  6147165 .   C   T   8.13869 .   DP=1;SGB=-0.379885;MQ0F=
scaffold20  6147166 .   A   G   8.13869 .   DP=1;SGB=-0.37

I need to print if the 4 column has this sequence

  A
  A
  G

Like this

scaffold20  6146785 .   A   G   8.13869 .   DP=1;SGB=-0.379885;MQ0
scaffold20  6146864 .   A   C   153 .   DP=7;VDB=0.637622;SGB
scaffold20  6146867 .   G   A   11.4845 .   DP=8;VDB=0.82;SGB=-0.45

Is there any option in Grep ?

4 Answers

Grepping vertically in a column is best done using awk. Here is a quick way of doing it.

awk -v s="AAG" 'BEGIN{n=length(s);t=sprintf("%*s",n," ")}
                {b[FNR%n]=$0; t=substr(t,2)$4}
                (t==s) { for(i=1;i<=n;++i) print b[(FNR+i)%n] }' file

The above works very nicely when you have to search for a specific string. When you want to search for a regular expression, you have to do things a bit differently. In case of large files, a double pass-algorithm is needed:

awk -v s="ERE" '(NR==FNR){t=t $4; next}
                (e<FNR) { if match(t,s) {b=RSTART+e;e=e+RSTART+RLENGTH-1} else exit}
                (b<=FNR && FNR<=e)' file file
  1. store 2 lines of history in an array
  2. Store the history of the 4th column in an array.
  3. If the current line and the two 4th columns from history match your condition.
    1. Print the history
    2. Print current line

Should work:

awk '
n==2 && c[0]=="A" && c[1]=="A" && $4=="G"{
    for (i=0;i<n;++i) print(h[i])
    print
}
{
     # shift history left
     # its just h[0]=h[1]; c[0]=c[1]; anyway
     for (i=0;i<n-1;++i) { h[i]=h[i+1]; c[i]=c[i+1]; }
     if (n < 2) n++;
     # append to history
     h[n-1]=$0; c[n-1]=$4; 
}' inputfile.txt

GENERIC solution: Where one could mention sequence which needs to be matched in 4th column of Input_file delimited with space and it will check it then.

awk -v sequence="A A G" '
BEGIN{
  num=split(sequence,array," ")
  for(i=1;i<=num;i++){
  counter[i]=array[i]
  }
}
FNR==NR{
  a[FNR]=$4
  val[FNR]=$0
  next
}
($4==counter[1]){
  found=1
  for(k=2;k<=num;k++){
    if(a[FNR+k-1]==counter[k]){ found++}
  }
}
found==num{
  print val[FNR]
  for(k=2;k<=num;k++){
     print val[FNR+k-1]
  }
  found=""
}
'  Input_file  Input_file


Very specific to OP's question: Could you please try following once, written and tested with shown samples in GNU awk, reading the Input_file 2 times to make it simple.

awk '
FNR==NR{
  a[FNR]=$4
  val[FNR]=$0
  next
}
($4=="A" && a[FNR+1]=="A" && a[FNR+2]=="G"){
  print val[FNR] ORS val[FNR+1] ORS val[FNR+2]
}
' Input_file   Input_file

Assuming a tab-delimited file and that the pattern constituents are only one character long, you can use grep, coreutils, and sed like this:

col=4
s=AAG
from=$(( 1 + $(cut -f$col infile | tr -d '\n' | grep -bo $s | cut -d: -f1) ))
to=$(( from + ${#s} - 1 ))
sed -n "$from,$to p" infile

Output:

scaffold20  6146785 .   A   G   8.13869 .   DP=1;SGB=-0.379885;MQ0
scaffold20  6146864 .   A   C   153     .   DP=7;VDB=0.637622;SGB
scaffold20  6146867 .   G   A   11.4845 .   DP=8;VDB=0.82;SGB=-0.45

If the file matches multiple times, you could generate a sed script, e.g. with separators between matches (this assumes GNU sed):

col=4
s=AAG
cut -f$col infile | tr -d '\n' | grep -bo $s | cut -d: -f1 | 
while read n; do 
  from=$(( n+1 ))
  to=$(( from + ${#s} - 1 ))
  echo $from,$to p
  echo "$to z"
  echo "$to s/^/--/p"
done | sed -nf - infile
Related