grep dokładny blok linii (zawartość pliku1) z pliku2


9

Mam dwa pliki file1i file2.

Przykładowa zawartość file1to:

A B
C D
E F
G H

a treść file2jest jak:

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

Chcę więc przeszukać tylko cały blok file1treści file2. Oznacza to, że dane wyjściowe powinny zawierać tylko następujące wiersze:

A B
C D
E F
G H

należy pamiętać, że: - tylko linie, które się zbliżają, powinny stanowić część wyniku.


Nie dostaję twojego pytania. Jeśli chcesz wydrukować tylko dokładną treść file1i nic więcej, po prostu użyj cat file1.
Wildcard

@Wildcard chce sprawdzić, czy plik2 zawiera dokładnie taką samą treść jak plik1. Pomyśl o tym, jakby szukał konkretnego rozdziału w książce
Sergiy Kolodyazhnyy,

Głosuję za ponownym otwarciem, ponieważ „członkowie zestawu” składają się z wielu linii (początkowo tego nie zauważyłem), co jest nieco bardziej złożone niż pojedyncze linie obsługiwane przez zaakceptowaną odpowiedź na proponowane duplikat pytania.
Kusalananda

1
Tu nie chodzi o zestawy. Jeśli chcesz oznaczyć to jako duplikat, przynajmniej znajdź inne pytanie o wyrażenia regularne z wieloma wierszami.
Michael Vehrs,

Odpowiedzi:


11

grepjest dość głupie, jeśli chodzi o wzory wielowierszowe, ale tłumaczenie wszystkich znaków nowego wiersza \nzarówno wzorca, jak i tekstu w celu wyszukania znaków NUL \0przed ich porównaniem naprawia to. Oczywiście konieczne jest także przetłumaczenie \0danych wyjściowych na \n.

Oto twoje polecenie, przy założeniu, że file1zawiera wzorzec, który chcesz wyszukać file2:

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

Przykładowe dane wyjściowe dla podanych plików:

A B
C D
E F
G H

Wyjaśnienie:

  • <(tr '\n' '\0' < file1)tworzy FIFO / nazwany potok / tymczasowy obiekt podobny do pliku, który jest równy file1, ale z wszystkimi znakami nowej linii przetłumaczonymi na znaki NUL.
  • <(tr '\n' '\0' < file2)robi to samo, ale dla file2.
  • grep -f PATTERN_FILE INPUT_FILEwyszukuje wzorce od PATTERN_FILEw INPUT_FILE.
  • -aFlaga grepumożliwia dopasowanie do plików binarnych. Jest to potrzebne, ponieważ w przeciwnym razie pomija pliki zawierające znaki niedrukowalne, takie jak \0.
  • -oFlaga grepczyni go wydrukować tylko sekwencję dopasowanie, a nie całej linii, gdzie został znaleziony.
  • | tr '\0' '\n' tłumaczy wszystkie znaki NUL z danych wyjściowych polecenia po lewej stronie z powrotem na znaki nowego wiersza.

6

Następujące jest niezdarne, ale działa z GNU awk:

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

3

Tylko dla zabawy w czystym bashu

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

Oto nieco bardziej elegancki grep+ perl:

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

Jest jednak jeden wielki haczyk. Jeśli istnieje w nowej linii spływu file1, wzór nie będzie poprawna, innymi słowy: A B\nC D\nE F\nG H\n\n.

(Specjalne podziękowania @terdon za udostępnienie części perla)

Jak zauważono, costas można użyć perl -0pe 's/\n(\n+$)?/\\n/g' zamiast drugiego perlpolecenia, aby uniknąć końcowego znaku nowej linii wfile1.txt


1
Jeśli jest ostatni znak nowej linii i nie jest to PO, chcę go znaleźć perl -0pe 's/\n(\n+$)?/\\n/g'. Bez -0tej gregex modyfikatora jest extra.
Costas

1

Nie jestem do końca pewien, jaki ma być wynik, ale łatwo to zrobić w językach, które nie są wyłącznie zorientowane liniowo (szczególnie jeśli oba pliki można odczytać do pamięci). Oto skrypt Pythona, który powie ci, ile jest dopasowań.

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

Chcesz wydrukować file1tyle razy, ile pasuje? Zastąp ostatni wiersz tym:

print(find * hay.count(find))

Możesz spakować wszystko do linii poleceń lub aliasu, jeśli naprawdę chcesz:

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

wynikiem będą wszystkie pliki z dokładnym dopasowaniem tekstu


0

Oto inne podejście z użyciem Pythona (przetestowane z python3 3.5.2, bez żadnych skarg od 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]))))

Obsługa argumentów wiersza poleceń za pośrednictwem sys.argvjest oczywiście uproszczona. Mógłbyś robić wiele innych rzeczy z wartością zwracaną finderna dwóch memoryviewobiektach, które przekazujesz, poza tym przekazywać je do tuple. Każdy SRE_Matchelement zwrócony przez iterator zwracany przez finderma różne metody, których próbkowanie jest podsumowane w danych printwyjściowych ( spanna przykład informuje o zakresie bajtów każdego dopasowania).

Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.