Utilisation de «head» ou «tail» sur un fichier texte ÉNORME - 19 Go

15

J'ai un problème avec l'affichage de morceaux d'un très gros fichier texte. Ce fichier, d'environ 19 Go, est évidemment trop volumineux pour être visualisé par tout moyen traditionnel.

J'ai essayé head 1et tail 1( head -n 1et tail -n 1) avec les deux commandes assemblées de différentes manières (pour arriver à un morceau au milieu) sans succès. Ma machine Linux exécutant Ubuntu 9.10 ne peut pas traiter ce fichier.

Comment gérer ce fichier? Mon objectif ultime est d'affiner les lignes 45000000 et 45000100.

nicorellius
la source
Vous songez à écrire un script Python rapide pour lire les lignes et imprimer celles que j'ai besoin de classer, mais je peux imaginer que cela prend beaucoup de temps ...
nicorellius
Toutes les lignes ont-elles la même longueur?
Paul
@Paul - malheureusement, ils ne sont pas de la même longueur.
nicorellius
Vous pouvez essayer splitde faciliter l'utilisation du gros fichier.
iglvzx
1
D'accord. Tout traitement d'un fichier aussi volumineux prendra du temps, donc les réponses ci-dessous vous aideront. Si vous souhaitez extraire uniquement la pièce que vous recherchez et pouvez estimer approximativement où elle se trouve, vous pouvez utiliser ddpour obtenir le morceau que vous recherchez. Par exemple dd if=bigfile of=extractfile bs=1M skip=10240 count=5, extraira 5 Mo du fichier à partir du point 10 Go.
Paul

Réponses:

11

Vous devez utiliser sed.

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

Cela indique sedd'imprimer les lignes 45000000-45000100 inclus et de quitter la ligne 45000101.

Kyle Jones
la source
1
C'est encore très lent, presque comme la tête -45000000,45000100p bigfile | queue -100> lignes sauvegardées
Dmitry Polushkin
tail+|headest plus rapide de 10 à 15%.
Erich
4

Créez une base de données MySQL avec une seule table qui a un seul champ. Importez ensuite votre fichier dans la base de données. Cela rendra très facile la recherche d'une certaine ligne.

Je ne pense pas que quoi que ce soit d'autre pourrait être plus rapide (si headet taildéjà échoué). En fin de compte, l'application qui souhaite trouver une ligne ndoit parcourir l'intégralité du fichier jusqu'à ce qu'elle ait trouvé des nsauts de ligne. Sans une sorte de recherche (index de ligne à décalage d'octets dans le fichier), aucune meilleure performance ne peut être obtenue.

Étant donné la facilité avec laquelle il est possible de créer une base de données MySQL et d'y importer des données, je pense que c'est une approche viable.

Voici comment faire:

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 serait le fichier que vous souhaitez lire.

La syntaxe correcte pour importer un fichier avec des valeurs délimitées par des tabulations sur chaque ligne est:

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

Un autre avantage majeur de ceci est que si vous décidez ultérieurement d'extraire un autre ensemble de lignes, vous n'avez pas à attendre à nouveau des heures pour le traitement (sauf si vous supprimez la base de données bien sûr).

Der Hochstapler
la source
C'est donc une bonne solution. Je l'ai fait fonctionner avec la sedcommande ci-dessous et j'ai identifié mes lignes. Mais maintenant, j'ai une question complémentaire à laquelle la méthode de base de données peut être mieux adaptée. Je dois maintenant supprimer quelques centaines de lignes du fichier.
nicorellius
Je suis sûr que je sedpourrais le faire aussi. Bien sûr, si vous aviez les données dans la base de données, il serait futile d'exporter un nouveau fichier avec juste les lignes que vous voulez.
Der Hochstapler
Merci encore. J'ai pris la sedréponse (parce que cela m'a fait plaisir plus immédiatement; -) mais je vous ai donné un vote positif car j'utiliserai votre méthode à l'avenir. Je vous en suis reconnaissant.
nicorellius
1
Vous pouvez essayer d'ajouter un FIELDS TERMINATED BY '\n'à la LOAD DATAligne.
Der Hochstapler
1
Je suis désolé, il y a eu une erreur dans mon code. J'ai également ajouté la syntaxe correcte pour votre cas (testée cette fois).
Der Hochstapler
1

Deux bons vieux outils pour les gros fichiers sont joinet split. Vous pouvez utiliser l' --lines=<number>option Split with qui coupe le fichier en plusieurs fichiers d'une certaine taille.

Par exemple split --lines=45000000 huge_file.txt. Les parties résultantes seraient en xa, xb, etc. Ensuite, vous pouvez headla partie xb qui inclurait les lignes que vous vouliez. Vous pouvez également «joindre» des fichiers à un seul gros fichier.

Anssi
la source
Génial, merci, j'ai totalement oublié la commande split.
siliconrockstar
0

Vous disposez des bons outils, mais vous les utilisez incorrectement. Comme précédemment répondu à U&L, tail -n +X file | head -n Y(notez le +) est 10-15% plus rapide que sedpour les lignes Y commençant à X. Et commodément, vous n'avez pas à explicitement exitle processus comme avec sed.

tail lira et supprimera les premières lignes X-1 (il n'y a aucun moyen de contourner cela), puis lira et imprimera les lignes suivantes. head lira et imprimera le nombre de lignes demandé, puis quittera. Lorsque head quitte, tail reçoit un signal SIGPIPE et meurt, donc il n'aura pas lu plus que la valeur d'une taille de tampon (généralement quelques kilo-octets) de lignes du fichier d'entrée.

Erich
la source