У меня есть файл, который растет примерно на 200 000 строк в день, и он все состоит из блоков из трех строк как таковых:
1358726575123 # key
Joseph Muller # name
carpenter # job
9973834728345
Andres Smith
student
7836472098652
Mariah Anthony
dentist
Теперь у меня есть другой файл, из которого я извлекаю около 10000 шаблонов клавиш, например 1358726575123
. Затем я запускаю for
цикл с этими шаблонами и проверяю их по первому файлу. Если файл не содержит такого шаблона, я сохраняю шаблон в третьем файле для дальнейшей обработки:
for number in $(grep -o '[0-9]\{12\}' file2); do # finds about 10.000 keys
if ! grep -q ^$number$ file1; then # file1 is a huge file
printf "$number\n" >>file3 # we'll process file3 later
fi
done
Пример кода очищает огромный файл 10000 раз, и я запускаю этот цикл примерно раз в минуту в течение всего дня .
Поскольку огромный файл продолжает расти, что я могу сделать, чтобы сделать все это быстрее и сэкономить процессор? Интересно, поможет ли сортировка файла по его ключу (если так, как?) Или использование БД вместо простого текста ...
Ответы:
Этот ответ основан на
awk
ответе, опубликованном potong .Это в два раза быстрее, чем
comm
метод (в моей системе), для тех же 6 миллионов строк в основном файле и 10 тысяч ключей ... (теперь обновлен для использования FNR, NR)Несмотря на то, что она
awk
работает быстрее, чем ваша текущая система, и даст вам и вашему компьютеру (ам) некоторое передышку, имейте в виду, что когда обработка данных идет так же интенсивно, как вы описали, вы получите наилучшие общие результаты, переключившись на выделенную базу данных; например. SQlite, MySQL ...источник
file1 -> mainfile
иfile2 -> keys
с gawk и mawk, и он выводит неправильные ключи.awk
Позволяет читать в серии файлов .. В этом случае в этой серии есть 3 файла. Выходные данные идут вstdout
mainfile
, И он также будет печатать любые ключи изkeys
файла, который НЕ находится вmainfile
... Это, вероятно, то, что происходит ... (Я посмотрю немного дальше ...$RANDOM
для загрузки.Проблема, конечно, в том, что вы запускаете grep для большого файла 10000 раз. Вы должны прочитать оба файла только один раз. Если вы хотите остаться за пределами языков сценариев, вы можете сделать это следующим образом:
comm
отсортированные списки, чтобы получить то, что только во втором спискеЧто-то вроде этого:
См
man comm
.Если бы вы могли усекать большой файл каждый день (например, файл журнала), вы могли бы хранить кэш отсортированных чисел, и не нужно было бы каждый раз анализировать его целиком.
источник
{12}
... OP использовал 12, но ключи примера 13 длинные ...<(grep...sort)
где имена файлов.tail -n +$linenum
для вывода только самых последних данных. Таким образом, вы будете обрабатывать только приблизительно 200 000 строк каждый день ... Я только что проверил это с 6 миллионами строк в основном файле и 10 тысячами ключей ... время : реальные 0m0.016s, пользовательские 0m0.008s, sys 0m0.008sДа, обязательно используйте базу данных. Они созданы именно для таких задач.
источник
Это может работать для вас:
РЕДАКТИРОВАТЬ:
Исправленный скрипт, позволяющий использовать дубликаты и неизвестные ключи в обоих файлах, по-прежнему создает ключи из первого файла, отсутствующего во втором:
источник
С таким большим количеством данных вы должны действительно переключиться на базу данных. В то же время, чтобы достичь достойной производительности, вам нужно не искать по
file1
отдельности каждый ключ. Запустите один,grep
чтобы извлечь все неисключенные ключи одновременно. Так как этоgrep
также возвращает строки, которые не содержат ключа, отфильтруйте их.(
-Fx
означает буквально искать целые строки.-f -
означает читать список шаблонов из стандартного ввода.)источник
-v
(-Fxv
) может позаботиться об этом.comm
.Позвольте мне подтвердить то, что сказали другие: «Отведите тебя в базу данных!»
Есть MySQL бинарных файлов свободно доступных для большинства платформ.
Почему не SQLite? Это основано на памяти, загружая плоский файл при запуске, а затем закрывая его, когда вы закончите. Это означает, что если ваш компьютер выходит из строя или происходит процесс SQLite, то же самое происходит и со всеми данными.
Ваша проблема выглядит как пара строк SQL и будет выполняться за миллисекунды!
После установки MySQL (который я рекомендую по сравнению с другими вариантами) я бы выложил $ 40 за книгу поваров О'Рейли от Энтони Молинаро, в которой много проблемных образцов, начиная с простых
SELECT * FROM table
запросов и проходя через агрегаты и множественные объединения.источник
Я не уверен, что это именно тот результат, который вы ищете, но, вероятно, самый простой способ:
Вы также можете использовать:
Каждый из них создает временный файл шаблона, который используется для выделения чисел из большого файла (
file1
).источник
grep -vf
вместоgrep -f
.Я полностью согласен с тем, что вы получаете базу данных (MySQL довольно прост в использовании). До тех пор, пока вы не запустите это, мне нравится
comm
решение Ангуса , но так много людей пытаютсяgrep
и ошибаются, что я думал, что покажу (или хотя бы один) правильный способ сделать этоgrep
.Первый
grep
получает ключи. Третийgrep
(в<(...)
) берет все ключи, используемые в большом файле, и<(...)
передает его как файл в качестве аргумента-f
во второй grep. Это приводит к тому, что второйgrep
использует его как список строк для соответствия. Затем он использует это, чтобы сопоставить свой ввод (список ключей) из канала (первыйgrep
) и печатает все ключи, извлеченные из файла ключей, а не (-v
) большой файл.Конечно, вы можете сделать это с временными файлами, которые вы должны отслеживать и не забудьте удалить:
Это печатает все строки,
allkeys
которые не появляются вusedkeys
.источник
grep: Memory exhausted
comm
, в таком порядке.Ключевой файл не меняется? Тогда вам следует избегать поиска старых записей снова и снова.
С помощью
tail -f
вы можете получить вывод растущего файла.grep -f читает шаблоны из файла, одна строка как шаблон.
источник
Не собирался публиковать свой ответ, потому что я думал, что такой объем данных не должен обрабатываться сценарием оболочки, и правильный ответ для использования базы данных уже был дан. Но с тех пор есть 7 других подходов ...
Читает первый файл в памяти, затем ищет второй файл для чисел и проверяет, хранятся ли значения в памяти. Это должно быть быстрее, чем несколько
grep
секунд, если у вас достаточно памяти для загрузки всего файла, то есть.источник
Я согласен с @ jan-steinman, что вы должны использовать базу данных для такого рода задач. Есть много способов собрать воедино решение с помощью сценария оболочки, как показывают другие ответы, но выполнение этого способа приведет к большим страданиям, если вы собираетесь использовать и поддерживать код в течение любого промежутка времени больше, чем просто однодневный одноразовый проект.
Предполагая, что вы используете Linux, у вас, скорее всего, установлен Python по умолчанию, который включает библиотеку sqlite3, начиная с Python v2.5. Вы можете проверить свою версию Python с:
Я рекомендую использовать библиотеку sqlite3, потому что это простое файловое решение, которое существует для всех платформ (в том числе внутри вашего веб-браузера!) И не требует установки сервера. По сути, нулевая конфигурация и нулевое обслуживание.
Ниже приведен простой скрипт на python, который будет анализировать формат файла, который вы указали в качестве примера, а затем выполняет простой запрос «выбрать все» и выводит все данные, хранящиеся в БД.
Да, это означает, что вам нужно изучить некоторый SQL , но в конечном итоге это того стоит. Кроме того, вместо анализа ваших файлов журнала, возможно, вы могли бы записать данные непосредственно в базу данных sqlite.
источник
/usr/bin/sqlite3
работает одинаково для сценариев оболочки ( packages.debian.org/squeeze/sqlite3 ), хотя я никогда не использовал его./usr/bin/sqlite3
сценарии оболочки, однако я рекомендую избегать сценариев оболочки, за исключением простых одноразовых программ, и вместо этого использовать язык, такой как python, который лучше обрабатывает ошибки и его легче поддерживать и расширять.