Grep strings em um subgrupo de linhas no arquivo txt

3

Eu tenho um arquivo que se parece com isso

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

Agora, para cada bloco de linhas que têm a mesma sequência na coluna 3 (por exemplo, x_00004), quero grep apenas as linhas que contêm strings específicas, se elas estiverem presentes juntas no bloco.

Então, eu sei que posso usar %código% Mas não consigo encontrar uma maneira de aplicar a primeira ação. Eu acho que grep -f <file containing string> <file to scan> vai me ajudar aqui, mas eu não sei como.

Eu gostaria de ter algo como:

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

Então, basicamente, as linhas contendo awk ou PF13304.1 somente serão exibidas se elas estiverem compartilhando o campo 3.

Eu uso PF13401.1 e PF13304.1 como exemplo, porque às vezes eu procuro a presença de 3 strings no bloco. Um problema é que a string que estou procurando nem sempre é consecutiva no arquivo que eu quero verificar.

Todas as strings que eu quero PF13401.1 também são reportadas em um arquivo txt. Eu posso organizá-los como quero corresponder ao comando grep .

Em vez disso, a linha que contém

AAA_21               PF13304.1  x_00006
AAA_22               PF13401.1  x_00007

Não deve ser incluído porque as strings que eu quero grep não compartilham o campo 3, o que significa que elas não estão presentes nos subgrupos grep ou x_00006

Então, do ponto de vista lógico, eu quero

  1. abra o arquivo
  2. divida as linhas em grupos de acordo com o campo 3, crie grupos que tenham a mesma string no campo 3
  3. neste subgrupo x_00007 as strings que estou procurando somente se elas estiverem presentes em cada bloco
por efrem 16.02.2015 / 12:38

5 respostas

1

Pode ser feito em Python com bastante facilidade:

$ 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

Conteúdo 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()

A maneira como essas palavras são redirecionadas o conteúdo do arquivo de entrada para o fluxo de stdin do script, lê o fluxo linha por linha e para cada linha fazemos uma pesquisa na lista de argumentos que fornecemos na linha de comando. Abordagem bastante simples

    
por Sergiy Kolodyazhnyy 25.12.2016 / 00:04
0

Certamente não é tão simples quanto grep . Este programa:

  • verifica o arquivo de texto, acumulando "blocos" em que o terceiro campo é a mesma string
  • quando encontrar um bloco, invocar grep e coletar a saída
  • se o número de linhas na saída for o mesmo que o número de termos de pesquisa, a saída do grep de saída
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 

produz esta saída:

AAA_21               PF13304.1  x_00004
AAA_22               PF13401.1  x_00004
AAA_21               PF13304.1  x_00005
AAA_22               PF13401.1  x_00005
    
por glenn jackman 16.02.2015 / 21:37
0

Então, se eu entendi corretamente, você quer encontrar todos os subgrupos que contêm todos os padrões que você especificar. Isso pode ser feito com sort e awk , por exemplo:

# 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=

Saída:

x_00004
x_00005

Com base na saída acima, agora você pode extrair as linhas relevantes de infile , por exemplo. adicione o seguinte ao canal acima:

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

Saída:

AAA_21               PF13304.1  x_00004
AAA_22               PF13401.1  x_00004
AAA_21               PF13304.1  x_00005
AAA_22               PF13401.1  x_00005
    
por Thor 18.02.2015 / 09:22
0

O seguinte script awk corresponde a literal strings listadas uma por linha em match_file , em relação a 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

Saída:

AAA_21               PF13304.1  x_00004
AAA_22               PF13401.1  x_00004
AAA_21               PF13304.1  x_00005
AAA_22               PF13401.1  x_00005
    
por Peter.O 22.08.2015 / 12:45
0

Algo parecido com isto?

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

ou isto, principalmente o mesmo, mas com um agrupamento mais legível

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

Exemplo

O arquivo de entrada

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

O comando

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
    
por A.B. 22.08.2015 / 13:05