Suppression efficace de l'en-tête sur place pour les fichiers volumineux à l'aide de sed?


24

Les commandes ci-dessous peuvent prendre quelques minutes en fonction de la taille du fichier. Existe-t-il une méthode plus efficace?

sed -i 1d large_file 

Réponses:


34

Essayez edplutôt:

ed <<< $'1d\nwq' large_file

Si ce «grand» signifie environ 10 millions de lignes ou plus, mieux vaut l'utiliser tail. N'est pas capable de montage sur place, mais ses performances rendent ce manque pardonnable:

tail -n +2 large_file > large_file.new

Modifiez pour afficher certaines différences de temps:

( awkcode de Jaypal ajouté pour avoir des temps d'exécution sur la même machine (CPU 2,2 GHz).)

bash-4.2$ seq 1000000 > bigfile.txt # further file creations skipped

bash-4.2$ time sed -i 1d bigfile.txt
time 0m4.318s

bash-4.2$ time ed -s <<< $'1d\nwq' bigfile.txt
time 0m0.533s

bash-4.2$ time perl -pi -e 'undef$_ if$.==1' bigfile.txt
time 0m0.626s

bash-4.2$ time { tail -n +2 bigfile.txt > bigfile.new && mv -f bigfile.new bigfile.txt; }
time 0m0.034s

bash-4.2$ time { awk 'NR>1 {print}' bigfile.txt > newfile.txt && mv -f newfile.txt bigfile.txt; }
time 0m0.328s

Dans ce cas tail, je préfère compter le temps pour faire à la fois supprimer la première ligne et remplacer bigfile.txtpar bigfile.new.
rozcietrzewiacz

@rozcietrzewiacz, votre point est correct. Merci. Mis à jour.
manatwork

C'est vraiment cool! J'ai fait la même chose avec awket j'ai obtenu le résultat suivant -[jaypal:~/Temp] seq 1000000 > bigfile.txt [jaypal:~/Temp] time awk 'NR>1 {print}' bigfile.txt >newfile.txt real 0m0.649s user 0m0.601s sys 0m0.033s
jaypal singh

1
@Jaypal, j'ai ajouté votre code à la liste des alternatives. Sur ma machine, c'était encore plus rapide. Étrange, je m'attendais à awkune performance plus proche de celle sedde. (Remarque pour moi-même: ne vous attendez jamais - testez à la place.)
Manatwork

C'était la meilleure solution dans mon cas: tail -n +2 bigfile.txt > bigfile.new && mv -f bigfile.new bigfile.txt;j'utilise un seul fichier avec un verrou pour garder une trace d'une seule liste de tâches utilisée par plusieurs processus. J'ai commencé avec ce que l'affiche initiale utilisée: sed -i 1d large_file . Cela provoquait le verrouillage du fichier pendant 1 à 2 secondes. Le tail/mvcombo se termine presque instantanément. Merci!
Chris Adams

6

Il n'y a aucun moyen de supprimer efficacement des éléments au début d'un fichier. La suppression des données depuis le début nécessite la réécriture de tout le fichier.

La troncature à partir de la fin d'un fichier peut cependant être très rapide (le système d'exploitation n'a qu'à ajuster les informations sur la taille du fichier, éventuellement en supprimant les blocs non utilisés). Ce n'est généralement pas possible lorsque vous essayez de supprimer de la tête d'un fichier.

Cela pourrait théoriquement être "rapide" si vous supprimiez exactement un bloc / une étendue, mais il n'y a pas d'appels système pour cela, vous devrez donc vous fier à la sémantique spécifique au système de fichiers (si cela existe). (Ou avoir une certaine forme de décalage à l'intérieur du premier bloc / étendue pour marquer le début réel du fichier, je suppose. Je n'en ai jamais entendu parler non plus.)


Si le fichier est très volumineux, la surcharge d'E / S est susceptible d'être (peut-être beaucoup) supérieure à la surcharge du processeur requise pour traiter la fin des lignes.
Mat

Vous avez raison. Cependant, il peut y avoir une différence dans la façon dont les outils accèdent au contenu du fichier. Le mieux n'est pas de traiter ligne par ligne quand ce n'est pas nécessaire ou du moins de ne pas lire ligne par ligne quand ce n'est pas nécessaire.
manatwork

2
Je suis surpris que la différence soit si grande dans vos résultats et puisse la reproduire avec cette taille de fichier ici. Les avantages semblent diminuer à mesure que la taille du fichier augmente (essayé avec seq 10M, 15s pour sed, 5s pour ed). Bons conseils quand même (+1).
Mat

À partir de la version 3.15, Linux dispose désormais d'une API pour réduire des parties d'un fichier sur des systèmes de fichiers basés sur une certaine étendue, mais au moins pour ext4 qui ne peut être fait que sur des blocs complets (généralement 4k).
Stéphane Chazelas

Même si l'édition nécessite la réécriture de l'intégralité du fichier, il est parfois très pratique d'avoir des outils en ligne de commande pour éditer efficacement. Dans mon cas, cela a aidé lorsque j'ai dû supprimer la première ligne d'un fichier qui était plus grande que la RAM totale de mon système.
Jason

3

La méthode la plus efficace, ne le faites pas! Si vous le faites, dans tous les cas, vous avez besoin du double du «grand» espace sur le disque et vous gaspillez les E / S.

Si vous êtes bloqué avec un gros fichier que vous souhaitez lire sans la 1ère ligne, attendez d'avoir besoin de le lire pour supprimer la 1ère ligne. Si vous devez envoyer le fichier de stdin à un programme, utilisez tail pour le faire:

tail -n +2 | your_program

Lorsque vous devez lire le fichier, vous pouvez en profiter pour supprimer la 1ère ligne, mais uniquement si vous disposez de l'espace nécessaire sur le disque:

tail -n +2 | tee large_file2 | your_program

Si vous ne pouvez pas lire depuis stdin, utilisez un fifo:

mkfifo large_file_wo_1st_line
tail -n +2 large_file > large_file_wo_1st_line&
your_program -i large_file_wo_1st_line

encore mieux si vous utilisez bash, profitez de la substitution de processus:

your_program -i <(tail -n +2 large_file)

Si vous avez besoin de rechercher dans le fichier, je ne vois pas de meilleure solution que de ne pas rester coincé avec le fichier en premier lieu. Si ce fichier a été généré par stdout:

large_file_generator | tail -n +2 > large_file

Sinon, il y a toujours la solution de substitution fifo ou process:

mkfifo large_file_with_1st_file
large_file_generator -o large_file_with_1st_file&
tail -n +2 large_file_with_1st_file > large_file_wo_1st_file

large_file_generator -o >(tail -n 2+ > large_file_wo_1st_file)

1

Vous pouvez utiliser Vim en mode Ex:

ex -sc '1d|x' large_file
  1. 1 sélectionner la première ligne

  2. d supprimer

  3. x sauver et fermer


0

C'est juste de la théorie, mais ...

Un système de fichiers personnalisé (implémenté à l'aide de FUSE ou d'un mécanisme similaire) pourrait exposer un répertoire dont le contenu est exactement le même qu'un répertoire déjà existant ailleurs, mais avec des fichiers tronqués comme vous le souhaitez. Le système de fichiers traduirait tous les décalages de fichiers. Ensuite, vous n'auriez pas à réécrire un fichier avec beaucoup de temps.

Mais étant donné que cette idée est très simple, à moins que vous n'ayez des dizaines de téraoctets de tels fichiers, la mise en œuvre d'un tel système de fichiers serait trop coûteuse / longue pour être pratique.

En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.