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

9

Eu tenho dois arquivos file1e file2.

O conteúdo da amostra 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

Quero pesquisar apenas todo o bloco de file1conteúdo file2. 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 unindo devem fazer parte da saída.

sachin
fonte
Eu não entendi sua pergunta. Se você deseja apenas imprimir o conteúdo exato file1e nada mais, basta usar cat file1.
Wildcard
@Wildcard ele quer ver se o arquivo2 contém exatamente o mesmo conteúdo que o arquivo1. Pense nisso como se
procurasse
Estou votando para reabrir isso, já que os "membros do conjunto" são compostos de várias linhas (eu não percebi isso a princípio), o que é um pouco mais complexo do que as linhas únicas tratadas pela resposta aceita à pergunta duplicada proposta.
Kusalananda
1
Isto é não sobre conjuntos. Se você quiser marcar isso como duplicado, encontre pelo menos outra pergunta sobre regexps de várias linhas.
Michael Vehrs

Respostas:

11

grepé bastante estúpido quando se trata de padrões de múltiplas linhas, mas a conversão de todos os caracteres \nde nova linha do padrão e do texto para pesquisar em caracteres NUL \0antes de compará-los corrige isso. Também é necessário traduzir \0a saída de volta para \n.

Aqui está o seu comando, supondo que file1contenha o padrão em que você deseja pesquisar file2:

grep -aof <(tr '\n' '\0' < file1) <(tr '\n' '\0' < file2) | tr '\0' '\n'

Exemplo de saída para os arquivos fornecidos:

A B
C D
E F
G H

Explicação:

  • <(tr '\n' '\0' < file1)cria um objeto FIFO / pipe nomeado / semelhante a arquivo temporário igual a file1, mas com todos os caracteres de nova linha traduzidos para caracteres NUL.
  • <(tr '\n' '\0' < file2)faz o mesmo, mas para file2.
  • grep -f PATTERN_FILE INPUT_FILEpesquisa os padrões a partir PATTERN_FILEde INPUT_FILE.
  • O -asinalizador de grephabilita a correspondência em arquivos binários. Isso é necessário porque, caso contrário, pularia arquivos que contenham caracteres não imprimíveis, como \0.
  • A -obandeira de grepfaz com que imprima apenas a sequência correspondente, não a linha inteira em que foi encontrada.
  • | tr '\0' '\n' converte todos os caracteres NUL da saída do comando no lado esquerdo para caracteres de nova linha.
Byte Commander
fonte
6

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

awk -v RS="$(<file1)" '{print RT}' file2
Michael Vehrs
fonte
3

Apenas por diversão em pura festa

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
Costas
fonte
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á uma grande captura. Se houver uma nova linha à direita no file1, o padrão não será correta, ou seja: A B\nC D\nE F\nG H\n\n.

(Agradecimentos especiais @terdon por fornecer a parte perl)

Como costas observou, um pode ser usado perl -0pe 's/\n(\n+$)?/\\n/g' no lugar do outro perlcomando para evitar a nova linha à direita nofile1.txt

Sergiy Kolodyazhnyy
fonte
1
Se houver uma nova linha à direita e esse não for o OP, deseja encontrar perl -0pe 's/\n(\n+$)?/\\n/g'. Sem -0o gregex, o modificador é extra.
Costas
1

Não tenho muita certeza do que você deseja que a saída seja, mas é fácil fazer com idiomas que não são exclusivamente orientados a linhas (especialmente se os dois 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")

Deseja imprimir file1quantas vezes corresponder? Substitua a última linha por esta:

print(find * hay.count(find))

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

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

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

meyerson
fonte
0

Aqui está outra abordagem usando python (testado com python3 3.5.2, sem queixas 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é reconhecidamente simplista. Você poderia fazer muitas outras coisas com o valor de retorno dos finderdois memoryviewobjetos pelos quais passa, além de passá-lo para tuple. Cada SRE_Matchitem gerado pelo iterador retornado por finderpossui vários métodos, cuja amostra é resumida na printsaída ( spanpor exemplo, informa o intervalo de bytes de cada correspondência).

Eirik Fuller
fonte