grep точный блок строк (содержимое файла1) из файла2

9

У меня есть два файла, file1и file2.

Пример содержания file1:

A B
C D
E F
G H

и содержание file2как:

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

Поэтому я хочу искать весь блок file1контента file2только. Это означает, что вывод должен содержать только эти строки:

A B
C D
E F
G H

обратите внимание, что: - только строки, которые собираются вместе, должны быть частью вывода.

Сэчин
источник
Я не понимаю ваш вопрос. Если вы хотите распечатать только точное содержимое file1и ничего больше, просто используйте cat file1.
Wildcard
@Wildcard он хочет видеть, содержит ли file2 точно такой же контент, как file1. Думайте об этом, как будто ищите конкретную главу в книге
Сергей Колодяжный,
Я голосую, чтобы снова открыть это, так как «набор членов» состоит из нескольких строк (я сначала этого не заметил), что несколько сложнее, чем отдельные строки, обработанные принятым ответом на предложенный дублирующий вопрос.
Кусалананда
1
Это не о наборах. Если вы хотите пометить это как дубликат, по крайней мере, найдите другой вопрос о регулярных выражениях с несколькими строками.
Майкл Вёрс

Ответы:

11

grepдовольно глупо, когда дело доходит до многострочных шаблонов, но перевод всех символов новой строки \nкак шаблона, так и текста для поиска в NUL-символы \0перед их сравнением исправляет это. Перевод \0в выводе обратно, \nочевидно, также необходим.

Вот ваша команда, предполагая, что она file1содержит шаблон, в котором вы хотите искать file2:

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

Пример вывода для заданных вами файлов:

A B
C D
E F
G H

Объяснение:

  • <(tr '\n' '\0' < file1)создает FIFO / именованный канал / временный файлоподобный объект, равный file1, но со всеми символами новой строки, переведенными в символы NUL.
  • <(tr '\n' '\0' < file2)делает то же самое, но для file2.
  • grep -f PATTERN_FILE INPUT_FILEищет шаблон (ы) из PATTERN_FILEin INPUT_FILE.
  • -aФлаг grepпозволяет согласование бинарных файлов. Это необходимо, потому что в противном случае он будет пропускать файлы, которые содержат непечатные символы, такие как \0.
  • -oФлаг grepделает его печать только последовательность согласования, а не вся линия , где она была найдена.
  • | tr '\0' '\n' переводит все NUL-символы из выходных данных команды слева обратно в символы новой строки.
Byte Commander
источник
6

Следующее неуклюже, но работает с GNU awk:

awk -v RS="$(<file1)" '{print RT}' file2
Майкл Верс
источник
3

Просто для удовольствия в чистом виде

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

Вот немного более элегантный grep+ perl:

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

Тем не менее, есть один большой улов. Если есть завершающий символ новой строка в file1, картина не будет правильной, другими слова A B\nC D\nE F\nG H\n\n.

(Отдельное спасибо @terdon за предоставленную часть Perl)

Как отмечалось в costas, можно использовать perl -0pe 's/\n(\n+$)?/\\n/g' вместо другой perlкоманды, чтобы избежать завершающего символа новой строки вfile1.txt

Сергей Колодяжный
источник
1
Если есть завершающий символ новой строки, и это не ОП, хотите найти perl -0pe 's/\n(\n+$)?/\\n/g'. Без -0в gрегулярных выражениях модификатора является дополнительным.
Костас
1

Я не очень уверен, что вы хотите, чтобы вывод был, но это легко сделать с языками, которые не являются исключительно ориентированными на строки (особенно если оба файла могут быть прочитаны в память). Вот скрипт Python, который скажет вам, сколько совпадений.

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

Вы хотите напечатать file1столько раз, сколько это соответствует? Замените последнюю строку на это:

print(find * hay.count(find))

Вы можете упаковать все в вызов командной строки или псевдоним, если вы действительно хотите:

python -c 'import sys; print("The text occurs", open(sys.argv[2]).read().count(open(sys.argv[1]).read()), "times")' file1 file2
Alexis
источник
1
grep -lir 'A B \n D C \n whatever' ./folder_to_search

Результатом будут все файлы с точным совпадением текста

Меерсон
источник
0

Вот еще один подход, использующий python (протестирован с python3 3.5.2, без нареканий 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]))))

Обработка аргументов командной строки через, sys.argvпо общему признанию, упрощена. Вы можете сделать много других вещей с возвращаемым значением finderдвух memoryviewпередаваемых вами объектов, кроме того, чтобы передать его tuple. Каждый SRE_Matchэлемент, возвращаемый итератором, возвращаемым функцией, finderимеет множество методов, выборка которых суммируется в printвыходных данных ( spanнапример, сообщает диапазон байтов каждого соответствия).

Эйрик Фуллер
источник