grep el bloque exacto de líneas (contenido del archivo1) del archivo2

9

Tengo dos archivos file1y file2.

El contenido de la muestra file1es:

A B
C D
E F
G H

y el contenido de file2es 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 lo tanto, quiero buscar solo en todo el bloque de file1contenido file2. Esto significa que la salida debe contener solo estas líneas:

A B
C D
E F
G H

tenga en cuenta que: - solo las líneas que se unen deben ser parte de la salida.

sachin
fuente
No entiendo tu pregunta. Si solo desea imprimir el contenido exacto file1y nada más, solo use cat file1.
Comodín
@Wildcard quiere ver si el archivo2 contiene exactamente el mismo contenido que el archivo1. Piénselo como si buscara un capítulo específico en un libro
Sergiy Kolodyazhnyy
Estoy votando para reabrir esto, ya que los "miembros del conjunto" están formados por varias líneas (no lo vi al principio), que es un poco más complejo que las líneas individuales manejadas por la respuesta aceptada a la pregunta duplicada propuesta.
Kusalananda
1
Esto no se trata de conjuntos. Si desea marcar esto como un duplicado, al menos encuentre otra pregunta sobre expresiones regulares de varias líneas.
Michael Vehrs

Respuestas:

11

grepes bastante estúpido cuando se trata de patrones multilínea, pero la traducción de todos los caracteres \nde nueva línea tanto del patrón como del texto para buscar caracteres NUL \0antes de compararlos soluciona esto. Obviamente, también es necesario traducir \0la salida a\n

Aquí está su comando, suponiendo que file1contiene el patrón en el que desea buscar file2:

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

Ejemplo de salida para sus archivos dados:

A B
C D
E F
G H

Explicación:

  • <(tr '\n' '\0' < file1)crea un objeto FIFO / canalización con nombre / archivo temporal que es igual file1, pero con todos los caracteres de nueva línea traducidos a caracteres NUL.
  • <(tr '\n' '\0' < file2)hace lo mismo, pero para file2.
  • grep -f PATTERN_FILE INPUT_FILEbusca los patrones de PATTERN_FILEin INPUT_FILE.
  • El -aindicador de grephabilita la coincidencia en archivos binarios. Esto es necesario porque de lo contrario se omitiría los archivos que contienen caracteres no imprimibles como \0.
  • El -oindicador de grephace que imprima solo la secuencia coincidente, no la línea completa donde se ha encontrado.
  • | tr '\0' '\n' traduce todos los caracteres NUL de la salida del comando en el lado izquierdo a caracteres de nueva línea.
Byte Commander
fuente
6

Lo siguiente es torpe, pero funciona con GNU awk:

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

Solo por diversión en pura fiesta

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
fuente
3

Aquí hay un poco más elegante grep+ perl:

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

Sin embargo, hay una gran trampa. Si hay un salto de línea final en file1, el patrón no será correcto, en otras palabras: A B\nC D\nE F\nG H\n\n.

(Un agradecimiento especial @terdon por proporcionar la parte perl)

Como señaló Costas, se puede usar perl -0pe 's/\n(\n+$)?/\\n/g' en lugar del otro perlcomando para evitar la nueva línea final en elfile1.txt

Sergiy Kolodyazhnyy
fuente
1
Si hay una nueva línea final y ese no es el OP que desea encontrar perl -0pe 's/\n(\n+$)?/\\n/g'. Sin -0el gmodificador regex es extra.
Costas
1

No estoy muy seguro de qué desea que sea la salida, pero es fácil de hacer con lenguajes que no están exclusivamente orientados a líneas (especialmente si ambos archivos se pueden leer en la memoria). Aquí hay un script de Python que le dirá cuántas coincidencias hay.

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

¿Desea imprimir file1tantas veces como corresponda? Reemplace la última línea con esto:

print(find * hay.count(find))

Puede empaquetar todo en una llamada de línea de comando o un alias, si realmente desea:

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

el resultado será todos los archivos con coincidencia exacta de texto

meyerson
fuente
0

Aquí hay otro enfoque usando python (probado con python3 3.5.2, sin quejas 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]))))

El manejo de los argumentos de la línea de comando a través sys.argves ciertamente simplista. Podría hacer muchas otras cosas con el valor de retorno de finderlos dos memoryviewobjetos que pasa, además de pasarlo tuple. Cada SRE_Matchelemento generado por el iterador devuelto por findertiene una variedad de métodos, una muestra de los cuales se resume en la printsalida (el span, por ejemplo, indica el rango de bytes de cada coincidencia).

Eirik Fuller
fuente