Использование 'head' или 'tail' в огромном текстовом файле - 19 ГБ

15

У меня проблема с просмотром фрагментов очень большого текстового файла. Этот файл, размером приблизительно 19 ГБ, очевидно, слишком велик для просмотра любыми традиционными способами.

Я попытался head 1и tail 1( head -n 1и tail -n 1) с обеими командами по-разному соединены (чтобы получить кусок в середине) без удачи. Моя Linux-машина с Ubuntu 9.10 не может обработать этот файл.

Как мне обработать этот файл? Моя конечная цель - отточить линии 45000000 и 45000100.

nicorellius
источник
Думая о написании быстрого скрипта Python для чтения строк и печати тех, которые мне нужны, но я могу себе представить, что это
займет
Все ли линии одинаковой длины?
Пол
@ Пол - к сожалению, они не одинаковой длины.
nicorellius
Вы можете попытаться splitоблегчить работу с большим файлом.
iglvzx
1
Ok. Любая обработка файла такого большого размера займет время, поэтому ответы ниже помогут в этом. Если вы хотите извлечь только ту часть, которую вы ищете, и можете приблизительно определить, где именно вы можете использовать ddто, что вам нужно. Например dd if=bigfile of=extractfile bs=1M skip=10240 count=5извлечет 5 МБ из файла, начиная с точки 10 ГБ.
Пол

Ответы:

11

Вы должны использовать sed.

sed -n -e 45000000,45000100p -e 45000101q bigfile > savedlines

Это говорит sedо необходимости печатать строки 45000000-45000100 включительно и выходить из строки 45000101.

Кайл Джонс
источник
1
Это все еще очень медленно, почти как голова -45000000,45000100p bigfile | tail -100> Сохраненные линии
Дмитрий Полушкин
tail+|headбыстрее на 10-15%.
Эрих
4

Создайте базу данных MySQL с одной таблицей, которая имеет одно поле. Затем импортируйте ваш файл в базу данных. Это позволит легко найти определенную строку.

Я не думаю, что что-то еще может быть быстрее (если headи tailуже не получится ). В конце концов, приложение, которое хочет найти строку n, должно выполнить поиск по всему файлу, пока не найдет nновые строки. Без какого-либо поиска (строковый индекс к байтовому смещению в файл) невозможно добиться лучшей производительности.

Учитывая, как легко создать базу данных MySQL и импортировать в нее данные, я чувствую, что это жизнеспособный подход.

Вот как это сделать:

DROP DATABASE IF EXISTS helperDb;
CREATE DATABASE `helperDb`;
CREATE TABLE `helperDb`.`helperTable`( `lineIndex` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT, `lineContent` MEDIUMTEXT , PRIMARY KEY (`lineIndex`) );
LOAD DATA INFILE '/tmp/my_large_file' INTO TABLE helperDb.helperTable (lineContent);
SELECT lineContent FROM helperTable WHERE ( lineIndex > 45000000 AND lineIndex < 45000100 );

/tmp/my_large_file будет файл, который вы хотите прочитать.

Правильный синтаксис для импорта файла с разделенными табуляцией значениями в каждой строке:

LOAD DATA INFILE '/tmp/my_large_file' INTO TABLE helperDb.helperTable FIELDS TERMINATED BY '\n' (lineContent);

Еще одним важным преимуществом этого является то, что, если вы решите позднее извлечь другой набор строк, вам не придется ждать часами обработки снова (если, конечно, вы не удалите базу данных).

Der Hochstapler
источник
Так что это действительно хорошее решение. Я получил его для работы с sedкомандой ниже и определил мои строки. Но теперь у меня есть дополнительный вопрос, для которого метод базы данных может быть лучше подходит. Теперь мне нужно удалить пару сотен строк из файла.
nicorellius
Я уверен, что sedмог бы сделать это также. Конечно, если бы у вас были данные в базе данных, было бы тривиально экспортировать новый файл с нужными строками.
Der Hochstapler
Еще раз спасибо. Я взял sedответ (потому что он доставил мне больше непосредственного удовольствия; -), но дал вам возможность проголосовать, потому что я буду использовать ваш метод в будущем. Я признателен за это.
nicorellius
1
Вы можете попробовать добавить FIELDS TERMINATED BY '\n'в LOAD DATAстроку.
Der Hochstapler
1
Извините, в моем коде произошла ошибка. Я также добавил правильный синтаксис для вашего случая (проверено на этот раз).
Der Hochstapler
1

Два старых добрых инструмента для больших файлов есть joinи split. Вы можете использовать разделение с --lines=<number>опцией, которая сокращает файл до нескольких файлов определенного размера.

Например split --lines=45000000 huge_file.txt. Полученные части будут представлены в виде xa, xb и т. Д. Затем вы можете получить headчасть xb, которая будет содержать нужные вам строки. Вы также можете «объединить» файлы обратно в один большой файл.

Анси
источник
Круто, спасибо, я полностью забыл про команду split.
silicrockstar
0

У вас есть нужные инструменты, но вы используете их неправильно. Как ранее ответили в U & L, tail -n +X file | head -n Y(обратите внимание, что +) на 10-15% быстрее, чем sedдля линий Y, начинающихся с X. И удобно, вам не нужно явно exitобрабатывать процесс как с sed.

tail будет читать и отбрасывать первые строки X-1 (пути назад нет), затем читать и печатать следующие строки. Глава прочитает и напечатает требуемое количество строк, затем выйдет. Когда голова выходит, tail получает сигнал SIGPIPE и умирает, поэтому он не будет считывать из входного файла строки размером больше буфера (обычно несколько килобайт).

Erich
источник