711 votes

Comment supprimer la première ligne d'un fichier texte en utilisant bash/sed script ?

J'ai besoin de supprimer de manière répétée la première ligne d'un énorme fichier texte en utilisant un script bash script.

Pour l'instant, j'utilise sed -i -e "1d" $FILE - mais cela prend environ une minute pour effectuer la suppression.

Existe-t-il un moyen plus efficace d'y parvenir ?

0 votes

Que veut dire -i ?

4 votes

@cikatomo : il s'agit de l'édition en ligne - il édite le fichier avec ce que vous générez.

4 votes

Tail est BEAUCOUP PLUS LENTE que sed. tail a besoin de 13.5s, sed de 0.85s. Mon fichier a ~1M de lignes, ~100MB. Macbook Air 2013 avec SSD.

1268voto

Aaron Digulla Points 143830

Essayez queue :

tail -n +2 "$FILE"

-n x : Il suffit d'imprimer la dernière x lignes. tail -n 5 vous donnerait les 5 dernières lignes de l'entrée. Le site + le signe inverse l'argument et fait tail imprimer tout sauf le premier x-1 lignes. tail -n +1 imprimerait le fichier entier, tail -n +2 tout sauf la première ligne, etc.

GNU tail est beaucoup plus rapide que sed . tail est également disponible sur BSD et le -n +2 est cohérent dans les deux outils. Vérifiez le FreeBSD o OS X pages de manuel pour en savoir plus.

La version BSD peut être beaucoup plus lente que sed cependant. Je me demande comment ils ont réussi à le faire ; tail devrait juste lire un fichier ligne par ligne alors que sed effectue des opérations assez complexes impliquant l'interprétation d'un script, l'application d'expressions régulières et autres.

Note : Vous pouvez être tenté d'utiliser

# THIS WILL GIVE YOU AN EMPTY FILE!
tail -n +2 "$FILE" > "$FILE"

mais cela vous donnera une fichier vide . La raison en est que la redirection ( > ) se produit avant tail est invoqué par le shell :

  1. Le shell tronque le fichier $FILE
  2. Shell crée un nouveau processus pour tail
  3. Le shell redirige le stdout du tail processus pour $FILE
  4. tail lit sur le site maintenant vide $FILE

Si vous voulez supprimer la première ligne à l'intérieur du fichier, vous devez utiliser :

tail -n +2 "$FILE" > "$FILE.tmp" && mv "$FILE.tmp" "$FILE"

El && permettra de s'assurer que le fichier ne sera pas écrasé en cas de problème.

3 votes

Selon cette ss64.com/bash/tail.html le tampon typique est fixé par défaut à 32k lorsque l'on utilise BSD 'tail' avec l'extension -r option. Peut-être qu'il y a un réglage de la mémoire tampon quelque part dans le système ? Ou -n est un nombre signé de 32 bits ?

0 votes

Hmm, ça vient de marcher pour moi sur un fichier de 92 M pour enlever les 400k+ premières lignes.

43 votes

@Eddie : user869097 a dit que cela ne fonctionne pas quand une simple est de 15Mb ou plus. Tant que les lignes sont plus courtes, tail fonctionnera pour n'importe quelle taille de fichier.

80voto

Nasri Najib Points 121

Pour ceux qui sont sur SunOS qui est non-GNU, le code suivant vous aidera :

sed '1d' test.dat > tmp.dat

41 votes

Une démographie intéressante

15voto

paxdiablo Points 341644

Non, c'est le plus efficace que vous puissiez faire. Vous pourriez écrire un programme C qui pourrait faire le travail un peu plus rapidement (moins de temps de démarrage et de traitement des arguments) mais il tendra probablement vers la même vitesse que sed lorsque les fichiers deviendront volumineux (et je suppose qu'ils sont volumineux si cela prend une minute).

Mais votre question souffre du même problème que tant d'autres, à savoir qu'elle présuppose la solution. Si vous nous disiez en détail ce que que vous essayez de faire plutôt que comment nous pourrons peut-être vous proposer une meilleure solution.

Par exemple, s'il s'agit d'un fichier A qu'un autre programme B traite, une solution serait de ne pas supprimer la première ligne, mais de modifier le programme B pour le traiter différemment.

Supposons que tous vos programmes ajoutent des éléments à ce fichier A et que le programme B lit et traite actuellement la première ligne avant de l'effacer.

Vous pourriez réorganiser le programme B de façon à ce qu'il n'essaie pas de supprimer la première ligne mais maintienne un décalage persistant (probablement basé sur le fichier) dans le fichier A de façon à ce que, lors de sa prochaine exécution, il puisse chercher ce décalage, y traiter la ligne et mettre à jour le décalage.

Ensuite, à un moment calme (minuit ?), il pourrait effectuer un traitement spécial du fichier A pour supprimer toutes les lignes en cours de traitement et remettre le décalage à 0.

Il sera certainement plus rapide pour un programme d'ouvrir et de rechercher un fichier plutôt que d'ouvrir et de réécrire. Cette discussion suppose que vous avez le contrôle du programme B, bien sûr. Je ne sais pas si c'est le cas mais il peut y avoir d'autres solutions possibles si vous fournissez des informations supplémentaires.

0 votes

Je pense que l'OP essaie d'atteindre ce qui m'a fait trouver cette question. J'ai 10 fichiers CSV contenant chacun 500 000 lignes. Chaque fichier a la même ligne d'en-tête que la première ligne. Je regroupe ces fichiers en un seul fichier, puis je les importe dans une base de données en laissant la base de données créer des noms de colonnes à partir de la première ligne. Il est évident que je ne veux pas que cette ligne soit répétée dans les fichiers 2 à 10.

4 votes

@d-b Dans ce cas, awk FNR-1 *.csv est probablement plus rapide.

10voto

Robert Gamble Points 41984

Comme l'a dit Pax, vous ne pourrez probablement pas aller plus vite que cela. La raison en est qu'il n'y a pratiquement aucun système de fichiers qui supporte la troncature à partir du début du fichier, donc cela va être un problème de O( n ) où n est la taille du fichier. Ce que vous pouvez faire beaucoup Le plus rapide est d'écraser la première ligne avec le même nombre d'octets (peut-être avec des espaces ou un commentaire), ce qui pourrait fonctionner pour vous en fonction de ce que vous essayez de faire exactement (qu'est-ce que c'est d'ailleurs ?).

1 votes

Re "...presque aucun système de fichiers qui supporte la troncature..." C'est intéressant ; pensez à inclure une note entre parenthèses pour nommer un tel système de fichiers.

5 votes

@agc : cela n'a plus rien à voir, mais mon premier emploi dans les années 70 était chez Quadex, une petite entreprise (aujourd'hui disparue, et sans rapport avec les deux sociétés qui utilisent maintenant ce nom). Ils avaient un système de fichiers qui permettait d'ajouter ou enlevant au début ou à la fin d'un fichier, utilisé principalement pour implémenter l'édition dans moins de 3KB en mettant au-dessus et au-dessous de la fenêtre dans les fichiers. Il n'avait pas de nom propre, il faisait simplement partie de QMOS, le système d'exploitation multi-utilisateurs de Quadex. ('Multi' était habituellement 2-3 sur un LSI-11/02 avec moins de 64KB de RAM et habituellement quelques disquettes 8" de type RX01 de 250KB chacune) :-)

10voto

alexis Points 10856

Vous puede modifier les fichiers en place : Utilisez simplement la fonction -i drapeau, comme ceci :

perl -ni -e 'print unless $. == 1' filename.txt

Cela fait disparaître la première ligne, comme vous le demandez. Perl devra lire et copier le fichier entier, mais il s'arrange pour que la sortie soit enregistrée sous le nom du fichier original.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X