Używanie „głowy” lub „ogona” na OGROMNYM pliku tekstowym - 19 GB

15

Mam problem z wyświetlaniem fragmentów bardzo dużego pliku tekstowego. Ten plik, około 19 GB, jest oczywiście zbyt duży, aby można go było wyświetlić w tradycyjny sposób.

Próbowałem head 1i tail 1( head -n 1i tail -n 1) obie komendy rurami ze sobą w różny sposób (aby dostać się na kawałek w środku) bez powodzenia. Mój komputer z systemem Linux i systemem Ubuntu 9.10 nie może przetworzyć tego pliku.

Jak obsłużyć ten plik? Moim ostatecznym celem jest udoskonalenie linii 45000000 i 45000100.

nicorellius
źródło
Zastanawiam się nad napisaniem szybkiego skryptu w Pythonie, aby odczytać wiersze i wydrukować te, które muszę złożyć, ale mogę sobie wyobrazić, że zajmuje to dużo czasu ...
nicorellius
Czy wszystkie linie mają tę samą długość?
Paul
@Paul - niestety nie są one tej samej długości.
nicorellius
Możesz spróbować splitułatwić pracę z dużym plikiem.
iglvzx
1
Dobrze. Każde przetwarzanie tak dużego pliku zajmie trochę czasu, więc pomocne będą poniższe odpowiedzi. Jeśli chcesz wyodrębnić tylko część, której szukasz, i możesz oszacować w przybliżeniu, gdzie to jest, możesz użyć, ddaby uzyskać kawałek, którego szukasz. Na przykład dd if=bigfile of=extractfile bs=1M skip=10240 count=5wyodrębni 5 MB z pliku, zaczynając od punktu 10 GB.
Paul

Odpowiedzi:

11

Powinieneś użyć sed.

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

To mówi, sedaby wydrukować linie 45000000-45000100 włącznie i wyjść z linii 45000101.

Kyle Jones
źródło
1
Nadal jest bardzo wolny, prawie jak główny plik -45000000,45000100p | tail -100> zapisane linie
Dmitry Polushkin
tail+|headjest szybszy o dobre 10-15%.
Erich,
4

Utwórz bazę danych MySQL z jedną tabelą, która ma jedno pole. Następnie zaimportuj plik do bazy danych. Ułatwi to wyszukiwanie określonej linii.

Nie sądzę, żeby cokolwiek innego mogło być szybsze (jeśli headi tailjuż zawodzi). W końcu aplikacja, która chce znaleźć linię, nmusi przeszukiwać cały plik, dopóki nie znajdzie nnowych linii. Bez pewnego rodzaju wyszukiwania (przesunięcie indeksu linii do bajtu w pliku) nie można osiągnąć lepszej wydajności.

Biorąc pod uwagę, jak łatwo jest utworzyć bazę danych MySQL i zaimportować do niej dane, wydaje mi się, że jest to realne podejście.

Oto jak to zrobić:

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 byłby plikiem, który chcesz przeczytać.

Prawidłowa składnia do importowania pliku z wartościami rozdzielanymi tabulatorami w każdym wierszu to:

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

Kolejną ważną zaletą tego jest to, że jeśli później zdecydujesz się wyodrębnić inny zestaw wierszy, nie musisz czekać godzin ponownie na przetwarzanie (chyba że oczywiście usuniesz bazę danych).

Der Hochstapler
źródło
To naprawdę dobre rozwiązanie. Mam go do pracy z sedponiższym poleceniem i zidentyfikowałem moje linie. Ale teraz mam pytanie uzupełniające, dla którego metoda bazy danych może być bardziej odpowiednia. Teraz muszę usunąć kilkaset wierszy z pliku.
nicorellius
Jestem pewien, że sedmógłbym to zrobić. Oczywiście, jeśli masz dane w bazie danych, wyeksportowanie nowego pliku z wybranymi liniami byłoby trywialne.
Der Hochstapler
Dzięki jeszcze raz. Przyjąłem sedodpowiedź (ponieważ dało mi to natychmiastową przyjemność; -), ale podniosłem głos, ponieważ użyję twojej metody w przyszłości. Doceniam to.
nicorellius
1
Możesz spróbować dodać a FIELDS TERMINATED BY '\n'do LOAD DATAlinii.
Der Hochstapler
1
Przepraszam, w moim kodzie był błąd. Dodałem również poprawną składnię dla twojego przypadku (tym razem przetestowane).
Der Hochstapler
1

Dwa dobre stare narzędzia do dużych plików to joini split. Możesz użyć podziału z --lines=<number>opcją cięcia pliku na wiele plików o określonym rozmiarze.

Na przykład split --lines=45000000 huge_file.txt. Wynikowe części będą w xa, xb itp. Następnie możesz headczęść xb, która zawierałaby żądane linie. Możesz także „połączyć” pliki z powrotem w pojedynczy duży plik.

Anssi
źródło
Wspaniale, dziękuję, całkowicie zapomniałem o poleceniu podziału.
siliconrockstar
0

Masz odpowiednie narzędzia, ale używasz ich nieprawidłowo. Jak już wcześniej odpowiedziano w U&L, tail -n +X file | head -n Y(zauważ, że +) jest 10-15% szybszy niż w sedprzypadku linii Y zaczynających się od X. I wygodnie, nie musisz jawnie exitproces jak z sed.

tail odczyta i odrzuci pierwsze linie X-1 (nie da się tego obejść), a następnie przeczyta i wydrukuje kolejne linie. head przeczyta i wydrukuje żądaną liczbę wierszy, a następnie wyjdzie. Kiedy głowa wychodzi, ogon odbiera sygnał SIGPIPE i umiera, więc nie będzie czytał więcej niż rozmiar bufora (zwykle kilka kilobajtów) linii z pliku wejściowego.

Erich
źródło