grep l'esatto blocco di linee (contenuto di file1) da file2


9

Ho due file file1e file2.

Il contenuto di esempio di file1è:

A B
C D
E F
G H

e il contenuto di file2è come:

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

Quindi voglio solo cercare l'intero blocco di file1contenuti file2. Ciò significa che l'output deve contenere solo queste righe:

A B
C D
E F
G H

si noti che: - solo le righe che si uniscono devono essere parte dell'output.


Non capisco la tua domanda. Se vuoi solo stampare il contenuto esatto file1e nient'altro, basta usare cat file1.
Wildcard l'

@Wildcard vuole vedere se file2 contiene esattamente lo stesso contenuto di file1. Pensaci come se
cercassi

Sto votando per riaprirlo poiché i "membri del set" sono composti da più righe (all'inizio non l'ho individuato), che è leggermente più complesso delle singole righe gestite dalla risposta accettata alla domanda duplicata proposta.
Kusalananda

1
Questo è non è sui set. Se vuoi contrassegnarlo come duplicato, almeno trova un'altra domanda sulle regexps multilinea.
Michael Vehrs,

Risposte:


11

grepè piuttosto stupido quando si tratta di schemi multilinea, ma la traduzione di tutti i caratteri \ndi nuova riga sia del modello che del testo per cercare in caratteri NUL \0prima di confrontarli risolve questo. Ovviamente è anche necessario tradurre \0nuovamente l'output in \n.

Ecco il tuo comando, supponendo che file1contenga lo schema in cui vuoi cercare file2:

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

Esempio di output per i tuoi file dati:

A B
C D
E F
G H

Spiegazione:

  • <(tr '\n' '\0' < file1)crea un oggetto simile a file FIFO / pipe / temporaneo uguale a file1, ma con tutti i caratteri di nuova riga tradotti in caratteri NUL.
  • <(tr '\n' '\0' < file2)fa lo stesso, ma per file2.
  • grep -f PATTERN_FILE INPUT_FILEcerca i motivi da PATTERN_FILEdentro INPUT_FILE.
  • Il -aflag di grepconsente la corrispondenza su file binari. Questo è necessario perché altrimenti salterebbe file che contengono caratteri non stampabili come \0.
  • La -obandiera di grepfa stampare solo la sequenza corrispondente, non l'intera riga in cui è stata trovata.
  • | tr '\0' '\n' traduce tutti i caratteri NUL dall'output del comando sul lato sinistro in caratteri di nuova riga.

6

Quanto segue è goffo, ma funziona con GNU awk:

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

3

Solo per divertimento in 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

3

Ecco un po 'più elegante grep+ perl:

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

Tuttavia, c'è un grosso problema. Se c'è una nuovalinea in file1, il modello non sarà corretta, in altre parole: A B\nC D\nE F\nG H\n\n.

(Un ringraziamento speciale a @terdon per aver fornito la parte perl)

Come notato da costas, si può usare perl -0pe 's/\n(\n+$)?/\\n/g' al posto dell'altro perlcomando per evitare la nuova riga finale nel filefile1.txt


1
Se c'è una nuova riga finale e quella non è l'OP, la vuole trovare perl -0pe 's/\n(\n+$)?/\\n/g'. Senza -0il gregex il modificatore è extra.
Costas,

1

Non sono troppo sicuro di ciò che vuoi che sia l'output, ma è facile fare con lingue che non sono esclusivamente orientate alla linea (specialmente se entrambi i file possono essere letti in memoria). Ecco uno script Python che ti dirà quante partite ci sono.

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

Vuoi stampare file1tutte le volte che corrisponde? Sostituisci l'ultima riga con questa:

print(find * hay.count(find))

Puoi mettere tutto in una riga di comando o alias, se davvero vuoi:

python -c 'import sys; print("The text occurs", open(sys.argv[2]).read().count(open(sys.argv[1]).read()), "times")' file1 file2

1
grep -lir 'A B \n D C \n whatever' ./folder_to_search

il risultato saranno tutti i file con corrispondenza esatta del testo


0

Ecco un altro approccio che usa python (testato con python3 3.5.2, senza lamentele da 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]))))

La gestione degli argomenti della riga di comando tramite sys.argvè certamente semplicistica. Potresti fare molte altre cose con il valore di ritorno dei finderdue memoryviewoggetti in cui passi, oltre a passarlo a tuple. Ogni SRE_Matcharticolo prodotto dall'iteratore restituito finderha una varietà di metodi, il cui campionamento è riassunto printnell'output (il span, ad esempio, indica l'intervallo di byte di ogni corrispondenza).

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.