Grep um parágrafo que combina palavras de um arquivo

3

Eu tenho um arquivo ( myfile ) organizado em parágrafos, ou seja, com linhas vazias separando as entradas. Eu quero recuperar alguns desses parágrafos de acordo com match .

Agora, tudo é divertido e divertido quando o jogo é apenas um: eu simplesmente faço awk -v RS='' '/match/ {print}' myfile , como em aqui . O fato é que tenho centenas de correspondências para encontrar em file , que coletei em outro arquivo ( matchfile ). Se eu tivesse que recuperar apenas a linha correspondente, faria um grep -f matchfile myfile .

Existe uma maneira de fazer algo semelhante a grep -f recuperar todo o parágrafo? Meu sabor Unix não suporta grep -p .

    
por LinuxBlanket 22.11.2016 / 16:28

2 respostas

2

Você pode transformar parágrafos em linhas únicas, usar grep -f matchfile no resultado e restaurar as novas linhas:

sed '/^$/s/^/\x02/' myfile | tr \n$'
sed '/^$/s/^/\x02/' myfile | tr \n$'%pre%2' $'%pre%3'\n \
| grep -f matchfile |  tr $'%pre%3' \n | head -n -1
2' $'%pre%3'\n \ | grep -f matchfile | tr $'%pre%3' \n | head -n -1

Você pode fazer sem o head se a linha vazia na saída não incomodar você.
Então ... sed adiciona \x02 ao início de cada linha vazia, então tr traduz todas as novas linhas para \x03 e todas \x02 para novas linhas (efetivamente transformando todos os parágrafos em linhas únicas onde as linhas originais são campos separado por algum caracter baixo ascii que é improvável que ocorra em seu arquivo de texto - nesse caso, \x03 ), então grep seleciona apenas as "linhas" correspondentes ; finalmente, o segundo tr restaura as novas linhas e head descarta a linha vazia à direita (você pode usar qualquer outra ferramenta, por exemplo, sed \$d ).
Realmente, a maneira mais fácil de entender como isso funciona é executá-lo em etapas: execute apenas o primeiro comando, depois o primeiro & 2º e assim por diante ... e observar a saída - deve ser auto-explicativo 1 .

1: se você se familiarizou com tr depois de ler o manual ...

    
por 24.11.2016 / 19:18
1

Vamos lá, não desista do awk tão rápido!

awk 'NR == FNR {
          aMatch[NR]=$0
          n=FNR
          next;
    }
    {
          RS="\n( |\t)*\n"
          for(i=1; i<n+1; i++) {
             if($0 ~ aMatch[i]) {
               print
               printf "\n"
               break                   
             }                 
          }
    }' matchFile myFile | head -n-1

Você pode querer colocar isso em um script:

awk -f myscript.awk matchFile myFile | head -n-1

A solução em formato de script awk , com anotações sobre o que faz:

# This block's instructions will only be executed for the first file (containing the lines to be matched)
# NR = number of line read, and FNR = number of line read in current file   
# So the predicate NR == FNR is only true when reading the first file !
NR == FNR {
   aMatch[NR]=$0          # Store the line to match in an array
   n=FNR                  # Store the number of matches
   next;                  # Tells awk to skip further instructions (they are intended for the other file) and process the next record
}
# We are now processing the second file (containing the paragraphs)
{
   RS="\n( |\t)*\n"          # Set record separator to a blank line. Instead of a single line, a record is now the entire paragraph
   for(i=1; i<n+1; i++) {    # Loop on all possible matches
      if($0 ~ aMatch[i]) {   # If $0 (the whole record i.e. paragraph) matches a line we read in file 1 then
         print               # Print the record (i.e. current paragraph)
         printf "\n"         # Print a new line to separate them. However we will end up with a trailing newline, hence the pipe to head -n-1 to get rid of it.
         break               # We need to break out of the loop otherwise paragraphs with n matches will be printed n times
      }                      # End of loop on matches
   }                         # End of 2nd file processing
}
    
por 22.11.2016 / 19:06