grep den genauen Zeilenblock (Inhalt von Datei1) aus Datei2

9

Ich habe zwei Dateien file1und file2.

Der Beispielinhalt von file1ist:

A B
C D
E F
G H

und der Inhalt von file2ist wie:

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

Ich möchte also nur den gesamten file1Inhaltsblock durchsuchen file2. Dies bedeutet, dass die Ausgabe nur die folgenden Zeilen enthalten sollte:

A B
C D
E F
G H

Bitte beachten Sie Folgendes: - Nur die Zeilen, die zusammenkommen, sollten Teil der Ausgabe sein.

Sachin
quelle
Ich verstehe deine Frage nicht. Wenn Sie nur den genauen Inhalt von file1und nichts anderes drucken möchten , verwenden Sie einfach cat file1.
Wildcard
@Wildcard möchte er sehen, ob Datei2 genau den gleichen Inhalt wie Datei1 enthält. Denken Sie darüber nach, als ob Sie nach einem bestimmten Kapitel in einem Buch suchen
Sergiy Kolodyazhnyy
Ich stimme dafür, dies wieder zu öffnen, da die "festgelegten Mitglieder" aus mehreren Zeilen bestehen (das habe ich zuerst nicht bemerkt), was etwas komplexer ist als einzelne Zeilen, die von der akzeptierten Antwort auf die vorgeschlagene doppelte Frage behandelt werden.
Kusalananda
1
Hier geht es nicht um Sets. Wenn Sie dies als Duplikat markieren möchten, finden Sie zumindest eine weitere Frage zu mehrzeiligen regulären Ausdrücken.
Michael Vehrs

Antworten:

11

grepist ziemlich dumm, wenn es um mehrzeilige Muster geht, aber das Übersetzen aller Zeilenumbrüche \nsowohl des Musters als auch des Textes, um \0vor dem Vergleich in NUL-Zeichen zu suchen , behebt dies. Eine Rückübersetzung \0in die Ausgabe nach \nist natürlich ebenfalls erforderlich.

Hier ist Ihr Befehl, vorausgesetzt, er file1enthält das Muster, in dem Sie suchen möchten file2:

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

Beispielausgabe für Ihre angegebenen Dateien:

A B
C D
E F
G H

Erläuterung:

  • <(tr '\n' '\0' < file1)Erstellt ein FIFO / file1Named Pipe / temporäres dateiähnliches Objekt, das gleich ist , wobei jedoch alle Zeilenumbruchzeichen in NUL-Zeichen übersetzt werden.
  • <(tr '\n' '\0' < file2)macht das gleiche, aber für file2.
  • grep -f PATTERN_FILE INPUT_FILEsucht nach den Mustern von PATTERN_FILEin INPUT_FILE.
  • Das -aFlag von grepaktiviert den Abgleich für Binärdateien. Dies ist erforderlich, da sonst Dateien übersprungen werden, die nicht druckbare Zeichen enthalten, wie z \0.
  • Mit dem -oFlag von grepwird nur die übereinstimmende Sequenz gedruckt, nicht die gesamte Zeile, in der sie gefunden wurde.
  • | tr '\0' '\n' übersetzt alle NUL-Zeichen von der Ausgabe des Befehls auf der linken Seite zurück in Zeilenumbruchzeichen.
Byte Commander
quelle
6

Folgendes ist ungeschickt, funktioniert aber mit GNU awk:

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

Nur zum Spaß in purer 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
Costas
quelle
3

Hier ist ein bisschen eleganter grep+ perl:

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

Es gibt jedoch einen großen Haken. Wenn ein nachfolgender Zeilenumbruch vorhanden ist file1, ist das Muster nicht korrekt, mit anderen Worten : A B\nC D\nE F\nG H\n\n.

(Besonderer Dank geht an @terdon für die Bereitstellung des Perl-Teils)

Wie bereits erwähnt, kann perl -0pe 's/\n(\n+$)?/\\n/g' anstelle des anderen perlBefehls der nachfolgende Zeilenumbruch im verwendet werdenfile1.txt

Sergiy Kolodyazhnyy
quelle
1
Wenn es einen nachgestellten Zeilenumbruch gibt und das nicht das OP suchen möchte perl -0pe 's/\n(\n+$)?/\\n/g'. Ohne -0den gRegex-Modifikator ist extra.
Costas
1

Ich bin mir nicht sicher, wie die Ausgabe aussehen soll, aber es ist einfach, Sprachen zu verwenden, die nicht ausschließlich zeilenorientiert sind (insbesondere, wenn beide Dateien in den Speicher eingelesen werden können). Hier ist ein Python-Skript, das Ihnen sagt, wie viele Übereinstimmungen es gibt.

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

Sie möchten file1so oft drucken, wie es passt? Ersetzen Sie die letzte Zeile durch:

print(find * hay.count(find))

Sie können alles in einen Befehlszeilenaufruf oder Alias ​​packen, wenn Sie wirklich möchten:

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

Das Ergebnis sind alle Dateien mit exakter Textübereinstimmung

Meyerson
quelle
0

Hier ist ein anderer Ansatz mit Python (getestet mit python3 3.5.2, ohne Beschwerden von 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]))))

Der Umgang mit Kommandozeilenargumenten über sys.argvist zugegebenermaßen vereinfacht. Sie können viele andere Dinge mit dem Rückgabewert von finderfür die beiden memoryviewObjekte tun, die Sie übergeben, außer es an zu übergeben tuple. Jedes SRE_MatchElement, das von dem von zurückgegebenen Iterator ausgegeben wird, finderverfügt über eine Vielzahl von Methoden, von denen eine Stichprobe in der printAusgabe zusammengefasst ist (die spanbeispielsweise den Bytebereich jeder Übereinstimmung angibt).

Eirik Fuller
quelle