grep o bloco exato de linhas (conteúdo do arquivo1) do arquivo2

9

Eu tenho dois arquivos, file1 e file2 .

O conteúdo de amostra de file1 é:

A B
C D
E F
G H

e o conteúdo de file2 é como:

A B
few other lines
E F
few more other lines
A B
C D
E F
G H
few more other lines
G H

Por isso, quero pesquisar o bloco inteiro de file1 content em file2 apenas. Isso significa que a saída deve conter apenas estas linhas:

A B
C D
E F
G H

observe que: - somente as linhas que estão se juntando devem ser a parte da saída.

    
por sachin 11.07.2016 / 05:59

7 respostas

11

grep é bastante estúpido quando se trata de padrões multilinhas, mas traduzir todos os caracteres newline \n do padrão e do texto para pesquisar em caracteres NUL \n antes de compará-los corrige isso. Também é necessário traduzir file1 na saída para file2 .

Este é o seu comando, supondo que <(tr '\n' 'file1' < file1) contenha o padrão que você deseja pesquisar em <(tr '\n' 'file2' < file2) :

grep -aof <(tr '\n' '
A B
C D
E F
G H
' < file1) <(tr '\n' '
grep -aof <(tr '\n' '
A B
C D
E F
G H
' < file1) <(tr '\n' '%pre%' < file2) | tr '%pre%' '\n'
' < file2) | tr '%pre%' '\n'

Exemplo de saída para seus arquivos:

%pre%

Explicação:

  • grep -f PATTERN_FILE INPUT_FILE cria um objeto FIFO / pipe nomeado / semelhante a um arquivo temporário que é igual a PATTERN_FILE , mas com todos os caracteres de nova linha traduzidos para caracteres NUL.
  • INPUT_FILE faz o mesmo, mas para -a .
  • grep procura o (s) padrão (s) de -o em grep .
  • O | tr '%code%' '\n' sinalizador de %code% ativa a correspondência em arquivos binários. Isso é necessário porque, do contrário, ele ignora arquivos que contêm caracteres não imprimíveis, como %code% .
  • O sinal %code% de %code% faz com que seja impressa apenas a sequência correspondente, não toda a linha em que foi encontrada.
  • %code% traduz todos os caracteres NUL da saída do comando no lado esquerdo de volta para os caracteres de nova linha.
por 11.07.2016 / 10:12
6

O seguinte é desajeitado, mas funciona com o GNU awk :

awk -v RS="$(<file1)" '{print RT}' file2
    
por 11.07.2016 / 09:20
3

Apenas por diversão em puro bash

mapfile -t <file1
while read line ; do
    [ "$line" = "${MAPFILE[i++]}" ] || { ["$line" = "$MAPFILE" ] && i=1 || i=0; }
    [ $i -eq ${#MAPFILE[*]} ] && { printf "%s\n" "${MAPFILE[@]}"; i=0; }
done <file2
    
por 11.07.2016 / 11:31
3

Aqui está um pouco mais elegante grep + perl :

$ grep -Pzo "$(perl -pe 's/\n/\n/g' file1.txt )"  file2.txt                    
A B
C D
E F
G H

No entanto, há um grande problema. Se houver uma nova linha à direita em file1 , o padrão não estará correto, em outras palavras: A B\nC D\nE F\nG H\n\n .

(Agradecimento especial @terdon por fornecer a parte do perl)

Como mencionado, é possível usar perl -0pe 's/\n(\n+$)?/\n/g' no lugar do outro comando perl para evitar a nova linha no file1.txt

    
por 11.07.2016 / 11:44
1

Não sei muito bem o que você quer que a saída seja, mas é fácil de fazer com linguagens que não são exclusivamente orientadas à linha (especialmente se ambos os arquivos puderem ser lidos na memória). Aqui está um script python que informa quantas correspondências existem.

import sys
find = open(sys.argv[1]).read()
hay = open(sys.argv[2]).read()
print("The text occurs", hay.count(find), "times")

Você deseja imprimir file1 quantas vezes forem correspondentes? Substitua a última linha por esta:

print(find * hay.count(find))

Você pode empacotar tudo em uma chamada de linha de comando ou alias, se você realmente quiser:

python -c 'import sys; print("The text occurs", open(sys.argv[2]).read().count(open(sys.argv[1]).read()), "times")' file1 file2
    
por 11.07.2016 / 11:01
1
grep -lir 'A B \n D C \n whatever' ./folder_to_search

o resultado será todos os arquivos com correspondência de texto exata

    
por 11.07.2016 / 19:44
0

Aqui está outra abordagem usando python (testado com python3 3.5.2 , sem reclamações de pylint3 1.5.6 ):

""" Locate entire file contents contiguous in other file """

import sys
import re
from mmap import mmap, PROT_READ

def memmap(name):
    """ Return memoryview of readonly mmap """
    with open(name, 'rb') as file:
        return memoryview(mmap(file.fileno(), 0, access=PROT_READ))

def finder(needle, haystack):
    """ Return iterator """
    return re.compile(re.escape(needle)).finditer(haystack)

print(tuple(finder(*(memmap(name) for name in sys.argv[1:3]))))

O tratamento de argumentos de linha de comando via sys.argv é assumidamente simplista. Você pode fazer muitas outras coisas com o valor de retorno de finder nos dois memoryview objetos que você passa, além de passá-lo para tuple . Cada SRE_Match item gerado pelo iterador retornado por finder possui uma variedade de métodos, uma amostragem que é resumida na print output (o span , por exemplo, informa o intervalo de byte de cada correspondência).

    
por 11.07.2016 / 22:48