web-dev-qa-db-fra.com

Chaînes Grep dans un sous-groupe de lignes dans un fichier txt

J'ai un fichier qui ressemble à ceci

AAA_21               PF13304.1  x_00004
AAA_22               PF13401.1  x_00004
SMC_N                PF02463.14 x_00004
AAA_29               PF13555.1  x_00004
DUF258               PF03193.11 x_00005
AAA_15               PF13175.1  x_00005
AAA_21               PF13304.1  x_00005
AAA_22               PF13401.1  x_00005
SMC_N                PF02463.14 x_00005
AAA_15               PF13175.1  x_00006
AAA_21               PF13304.1  x_00006
AAA_22               PF13401.1  x_00007
SMC_N                PF02463.14 x_00007

Maintenant, pour chaque bloc de lignes qui ont la même chaîne dans la colonne 3 (par exemple x_00004), je veux grep uniquement les lignes contenant des chaînes spécifiques si elles sont présentes ensemble dans le bloc.

Donc, je sais que je peux utiliser grep -f <file containing string> <file to scan> Mais je ne trouve pas de moyen d'appliquer la première action. Je suppose que awk m'aidera ici, mais je ne sais pas vraiment comment.

J'aimerais avoir quelque chose comme:

AAA_21               PF13304.1  x_00004
AAA_22               PF13401.1  x_00004
AAA_21               PF13304.1  x_00005
AAA_22               PF13401.1  x_00005

Donc, en gros, les lignes contenant PF13304.1 ou PF13401.1 uniquement s'ils partagent le champ 3.

J'utilise PF13304.1 et PF13401.1 comme exemple, car parfois je recherche la présence de 3 chaînes dans le bloc. Un problème est que la chaîne que je recherche n'est pas toujours consécutive dans le fichier que je souhaite scanner.

Toutes les chaînes que je veux grep sont également signalées dans un fichier txt. Je peux les organiser comme je veux faire correspondre la commande grep.

Au lieu de cela, la ligne contenant

AAA_21               PF13304.1  x_00006
AAA_22               PF13401.1  x_00007

Ne doit pas être inclus car les chaînes que je veux grep ne partagent pas le champ 3, ce qui signifie qu'elles ne sont pas toutes les deux présentes dans les sous-groupes x_00006 ou x_00007

Donc, du point de vue logique, je veux

  1. ouvrir le dossier
  2. divisez les lignes en groupes selon le champ 3, créez un groupe qui a la même chaîne dans le champ 3
  3. dans ces sous-groupes grep les chaînes que je recherche uniquement si elles sont toutes présentes dans chaque bloc
3
efrem

Peut être fait en Python assez facilement:

$ cat input.txt | ./find_strings.py PF13304.1 PF13401.1                                                                  
AAA_21               PF13304.1  x_00004
AAA_22               PF13401.1  x_00004
AAA_21               PF13304.1  x_00005
AAA_22               PF13401.1  x_00005
AAA_21               PF13304.1  x_00006
AAA_22               PF13401.1  x_00007

Contenu de find_strings.py:

#!/usr/bin/env python
import sys
strings=sys.argv[1:]
for line in sys.stdin:
    for string in strings:
         if string in line:
             print line.strip()

La façon dont ces mots sont que nous redirigeons le contenu du fichier d'entrée vers le flux stdin du script, lisons le flux ligne par ligne, et pour chaque ligne nous faisons une recherche dans la liste des arguments que nous fournissons en ligne de commande. Approche assez simple

1
Sergiy Kolodyazhnyy

Certainement pas aussi simple que grep. Ce programme:

  • scanne le fichier texte, accumulant des "blocs" où le 3ème champ est la même chaîne
  • quand il a trouvé un bloc, appelez grep et récupérez la sortie
  • si le nombre de lignes dans la sortie est le même que le nombre de termes de recherche, sortez la sortie de grep
awk '
  function grep(block,    m, grep_out, cmd, line, i) {
    m = 0
    delete grep_out

    cmd = "grep -f " ARGV[1]    # define the grep command
    print block |& cmd          # invoke grep, and send the block of text as stdin
    close(cmd, "to")            # close greps stdin so we can start reading the output

    # read from grep until no more output
    while ((cmd |& getline line) > 0)
      grep_out[m++] = line
    close(cmd)

    # did grep find all search terms?  If yes, print the output 
    if (length(grep_out) == nterms)
      for (i=0; i<m; i++) 
        print grep_out[i]
  }

  # read the search terms file, just to count the number of lines
  NR == FNR {
    nterms++
    next
  }

  # if we detect a new block, call grep and start a new block
  section != $3 {
    if (block) grep(block)
    block = ""
    section = $3
  } 

  {block = block $0 RS}   # accumulate the lines in this block

  END {if (block) grep(block)}       # also call grep at end of file

' fileContainingStrings fileToScan 

produit cette sortie:

AAA_21               PF13304.1  x_00004
AAA_22               PF13401.1  x_00004
AAA_21               PF13304.1  x_00005
AAA_22               PF13401.1  x_00005
0
glenn jackman

Donc, si je vous comprends bien, vous voulez trouver tous les sous-groupes qui contiennent TOUS les modèles que vous spécifiez. Cela peut être fait avec sort et awk, par exemple:

# make sure subgroups are adjacent 
sort -k3,3 infile |

# add a newline between subroups, this allows the next 
# invocation of awk to read each subgroup as a record
awk 'NR > 1 && p!=$3 { printf "\n" } { p=$3 } 1' |   

# match the desired patterns and print the subgroup name
awk '/\<PF13304\.1\>/ && /\<PF13401\.1\>/ { print $3 }' RS=

Production:

x_00004
x_00005

Sur la base de la sortie ci-dessus, vous pouvez maintenant extraire les lignes pertinentes de infile, par exemple ajoutez ce qui suit au tuyau ci-dessus:

while read sgrp; do
  grep -E "\b(PF13304\.1|PF13401\.1)\b +$sgrp\$" infile
done

Production:

AAA_21               PF13304.1  x_00004
AAA_22               PF13401.1  x_00004
AAA_21               PF13304.1  x_00005
AAA_22               PF13401.1  x_00005
0
Thor

Le script awk suivant correspond à littéral chaînes répertoriées une par ligne dans match_file, contre data_file

awk 'function endgroup() {
         gmc=0                              # group match count
         for( gi=1; gi<=gz; gi++ ) {        # step through all lines in a group
             split(group[gi],g)             # split one group line 
             for( lix in lms )              # for each literal match string index 
                 if( lix == g[2] )          # does literal match string = group record $2  
                     mrec[++gmc]=group[gi]  # group matched record array, and inc match count
         } 
         if( gmc==lmz ) for( mri=1; mri<=lmz; mri++ ) print mrec[mri]
         delete group; gz=0
     }

     BEGIN{ p3=FS } # an impossible previous value of $3 of "data_file"

     # process "match_file"
     NR==FNR { lms[$0]   # build array with literal match strings as indices
               lmz++     # literal match strings array size 
               next } 
     # process "data_file"
     p3!=$3 && p3!=FS { endgroup() }
     { group[++gz]=$0; p3=$3 }

     END{ if( p3!=FS ) endgroup() }
' match_file data_file

Production:

AAA_21               PF13304.1  x_00004
AAA_22               PF13401.1  x_00004
AAA_21               PF13304.1  x_00005
AAA_22               PF13401.1  x_00005
0
Peter.O

Quelque chose comme ça?

awk '(/x_00004/ || /x_00005/) && (/PF13401.1/ || /PF13304.1/)' your_file

ou ceci, principalement le même mais avec un regroupement plus lisible

awk '(/x_00004/ && (/PF13401.1/ || /PF13304.1/)) || (/x_00005/ && (/PF13401.1/ || /PF13304.1/))' your_file

Exemple

Le fichier d'entrée

cat foo

AAA_21               PF13304.1  x_00004
AAA_22               PF13401.1  x_00004
SMC_N                PF02463.14 x_00004
AAA_29               PF13555.1  x_00004
DUF258               PF03193.11 x_00005
AAA_15               PF13175.1  x_00005
AAA_21               PF13304.1  x_00005
AAA_22               PF13401.1  x_00005
SMC_N                PF02463.14 x_00005
AAA_15               PF13175.1  x_00006
AAA_21               PF13304.1  x_00006
AAA_22               PF13401.1  x_00007
SMC_N                PF02463.14 x_00007

La commande

awk '(/x_00004/ || /x_00005/) && (/PF13401.1/ || /PF13304.1/)' foo

AAA_21               PF13304.1  x_00004
AAA_22               PF13401.1  x_00004
AAA_21               PF13304.1  x_00005
AAA_22               PF13401.1  x_00005
0
A.B.