Проблема: у меня довольно большой репозиторий (тысячи файлов, сотни тысяч строк).

У меня есть текстовый файл с ~ 5000 строк.

Мне нужно найти строки в текстовом файле, которые не появляются где-либо еще в хранилище.

Есть ли инструмент или умный способ использовать grep, который может эффективно найти этот ответ?

Спасибо за любую помощь

1 ответ1

0

Решение было разработано в bash на Ubuntu 16.04.2 LTS.


Алгоритм

Этот раздел является образовательным. Вы можете найти весь сценарий в конце моего ответа.

Сначала сделайте копию вашего текстового файла. Это важно, файл, с которым мы будем работать, будет перезаписан, и для этого есть причина. Настройте переменные в соответствии с вашим случаем:

patterns="/path/to/your/text/copy"
repository="/path/to/your/repository/"

Вам понадобится несколько временных файлов.

tmpf1=`mktemp`
tmpf2=`mktemp`

Следующая команда сохранит все (ну, почти все, прочитайте вместе) шаблоны, которые появятся в хранилище, в первый временный файл. Смотрите man grep чтобы расшифровать команду. Также решите, нужно ли вам добавлять опцию -i в grep . Первый uniq является необязательным, он используется для предварительного сокращения данных, которые идут на sort .

grep -rhoIFf "$patterns" "$repository" | uniq | sort | uniq | tee "$tmpf1" | wc -l

Если приведенная выше команда выводит 0 , файл $patterns точно является вашим окончательным результатом, независимо от ошибок, указанных ниже, и вам следует только удалить временные файлы.

Существуют подводные камни с grep , вы будете иметь дело с ними в мгновение ока. Хорошо знать, кто они.

  1. Если в качестве шаблонов используются foobar и foo , foobar в репозитории будет соответствовать только foobar .
  2. Если в качестве шаблонов используются foobar и barbaz , foobarbaz в репозитории будет соответствовать только foobar .
  3. Если в качестве шаблонов есть foobarbaz и bar , foobarbaz в репозитории будет соответствовать только foobarbaz .

Из-за этих ловушек $tmpf1 может не содержать все шаблоны, которые действительно появляются в хранилище (то есть он может не содержать barbaz из второй ловушки).

Теперь вам нужно выбрать все те строки из $patterns , которые якобы не были найдены в репозитории. Обратите внимание, что вы должны соответствовать целые строки, следовательно, -x .

grep -vxFf "$tmpf1" "$patterns" > "$tmpf2"

В этот момент $tmpf2 будет вашим окончательным результатом, но из-за этих ошибок он может содержать слишком много строк (например, barbaz из второй ошибки). Хитрость заключается в том, чтобы использовать $tmpf2 в качестве нового файла шаблона и повторить процесс! Призовите:

cp "$tmpf2" "$patterns"

затем перейдите к первому grep . Повторяйте эту процедуру, пока не получите 0 от wc . Как я уже говорил, когда возвращается 0 ваш результат в $patterns .

В конце удалите временные файлы:

rm "$tmpf1" "$tmpf2"

КПД

У меня есть 200 тыс. Текстовых файлов, 4,5 млн строк, всего 300 мегабайт. Это HTML-документы с простыми заголовками и форматированием, практически текст на английском языке. Я взял 3k самых распространенных английских слов в качестве шаблонов и добавил несколько строчек «Мамбо-джамбо».

Сначала grep потребовалось несколько минут, чтобы прочитать данные с жесткого диска и работать, затем около двух минут для sort . Но каждая последующая итерация занимала считанные секунды благодаря кешированию и уменьшению $patterns .

Мое аппаратное обеспечение - Core i7 и 8 ГБ оперативной памяти. Ваши шаблоны и файлы могут значительно отличаться и влиять на время выполнения. Тем не менее, я думаю, что есть шанс, что вы решите задачу за несколько минут.


Сценарий

Это реализация вышеуказанного алгоритма. Еще одна дополнительная функция: он берет шаблоны из стандартного stdin , печатает результат на стандартном stdout . В этом случае вам не нужно копировать ваш текстовый файл. Сценарий не является надежным.

Сохраните следующий код как findUnused.sh , затем chmod a+x findUnused.sh .

#!/bin/bash

patterns=`mktemp`
cat > "$patterns"
repository="$1"
tmpf1=`mktemp`
tmpf2=`mktemp`

while [ `grep -rhoIFf "$patterns" "$repository" | uniq | sort | uniq | tee "$tmpf1" | wc -l` -ne 0 ]
do
  grep -vxFf "$tmpf1" "$patterns" > "$tmpf2"
  cp "$tmpf2" "$patterns"
done
cat "$patterns"
rm "$patterns" "$tmpf1" "$tmpf2"

Использование (обратите внимание, что есть перенаправления):

./findUnused.sh "/path/to/your/repository/" < "/path/to/your/text/file" > "/path/to/store/the/result"

Всё ещё ищете ответ? Посмотрите другие вопросы с метками .