Решение было разработано в 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
, вы будете иметь дело с ними в мгновение ока. Хорошо знать, кто они.
- Если в качестве шаблонов используются
foobar
и foo
, foobar
в репозитории будет соответствовать только foobar
.
- Если в качестве шаблонов используются
foobar
и barbaz
, foobarbaz
в репозитории будет соответствовать только foobar
.
- Если в качестве шаблонов есть
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"