Это @rozcietrzewiacz - отличное решение, но если Вы все еще хотите остаться с текстовыми файлами (как возвращено file
), можно тщательно создать массив имен файлов, затем выполнить Ваш grep
команда на том массиве.
Я предполагаю следующее:
file
util та поддержка -0
и -i
опции;\x
коды символа exadecimal.Вот пример
#!/bin/bash
get_file_list() {
local path="$1"
find "$path" -type f -exec file -0i {} + |
sed -n '/\x00 *text\//s/\x00.*//p'
}
list=()
while IFS= read -r line; do
list+=("$line")
done < <(get_file_list .)
# to choose options and pattern
grep -i pattern "${list[@]}"
sed
команда берет последовательность строки текста, прибывающего из file
, составленный из имени файла, байта NUL и типа пантомимы. Если во второй части (после NUL) существует слово text/
затем удалите ту часть и только распечатайте имя файла, иначе ничего не распечатайте.
Ключ к производительности читает огромный файл только однажды.
Можно передать несколько шаблонов grep путем помещения их на отдельные строки. Это обычно делается, говоря grep читать шаблоны из файла:
grep -F -f 300KFile 30MFile
Это производит соответствия в порядке большого файла и печатает строки, которые соответствуют нескольким шаблонам только однажды. Кроме того, это ищет шаблоны где угодно в строке; например, если файл шаблона содержит 1234
, затем строки такой как 123456,345678,2348962342
и 478912,1211138,1234
будет соответствовать.
Можно ограничить точными совпадениями столбца путем предварительной обработки шаблона. Например, если шаблоны не содержат специального символа ()?*+\|[]{}
:
<300KFile sed -e 's/^/(^|,)/' -e 's/$/($|,)/' |
grep -E -f - 30MFile
Если сохранение только первого соответствия для каждого шаблона важно, сделайте первичную обработку для извлечения только соответствующих строк как выше, затем сделайте вторую передачу в awk или жемчуге, который отслеживает шаблоны, которые были уже замечены.
<300KFile sed -e 's/^/(^|,)/' -e 's/$/($|,)/' |
grep -E -f - 30MFile |
perl -l -F, -ape '
BEGIN {
open P, "300KFile" or die;
%patterns = map {chomp; $_=>1} <P>;
close P;
}
foreach $c (@F) {
if ($patterns{$c}) {
print;
delete $patterns{$c};
}
}
'
Можно ли выполнить следующее?
grep -Ff FILE_A FILE_B > FILE_C
Теперь можно запустить скрипт на файлах A и C только.
Обновление: Ожидать... Это сохраняет порядок?
Другое обновление: еще Некоторая обработка необходима для хранения порядка. Это дает мне те же результаты как Ваш исходный сценарий. Протестированный на 300K строках в FILE_A и только 300K строки в FILE_B, 125 минут по сравнению с 14 secs.
#! /bin/bash
grep -Ff FILE_A FILE_B > FILE_B_TMP
grep -oFf FILE_A FILE_B_TMP > FILE_A_SHUFF
grep -Ff FILE_A_SHUFF FILE_A > FILE_A_TMP
while read -r line; do
grep -F -m1 "$line" FILE_B_TMP
done < FILE_A_TMP > result.txt
Я полагаю, что коммуникация может сделать лучшую производительность:
comm -12 300KFile <(sed 's/,.*//' 30MFile)
PS. Я не уверен если строка 123123
fromm 300KFile должен соответствовать для строкового представления gdwyedg,123123,hfsjdkfh
в 30M файл. Как в Вашем сценарии это соответствует, но в моем сценарии это не делает.
Я полагаю, что grep базировался, решения все еще должны сравнить каждую запись в FILE_A с каждой записью в FILE_B. С тех пор, по крайней мере, n-1 записи в FILE_A не будет соответствовать определенной записи в FILE_B, существует большое дублирование в этом подходе. Если бы с другой стороны, файлы были отсортированы затем, то было бы возможно отбросить большое количество тестов для каждого сравнения. Следовательно, что-то как....
#!/bin/bash
# NB a faster solution would be to sort the smaller file in a seperate process
# you might also want to set a buffer size for large files
sort $1 > /tmp/$$.a
sort $2 > /tmp/$$.b
join -j1 -t',' /tmp/$$.a /tmp/$$.b
rm -f /tmp/$$.?
(не протестированный)
Но обратите внимание, что порядок записей будет изменен, это предполагает, что Вы хотите соответствовать на определенном столбце данных в FILE_B, и также сортировка представляет издержки - но результат должен быть быстрее для этих размеров файла.
300KFile
? Это соответствовало бы всем строкам в30MFile
. – Gilles 'SO- stop being evil' 30.05.2012, 11:11