En face de la queue: toutes les lignes sauf les n dernières lignes

36

Comment puis-je supprimer les n dernières lignes d'un fichier avec un filtre de ligne de commande Unix?

Ce serait en quelque sorte le contraire de tail: tailIgnore les n premières lignes , mais le reste des tuyaux à travers, mais je veux la commande de conduite tout à travers sauf les n dernières lignes.

Malheureusement, je n'ai rien trouvé de tel - ça headn'aide pas non plus. EDIT : au moins dans Solaris, il ne prend pas d'arguments négatifs.

Mise à jour: je suis surtout intéressé par une solution qui fonctionne pour les gros fichiers, c'est-à-dire les fichiers journaux, où vous voudrez peut-être inspecter ce qui s'est passé au cours des dernières minutes.

Hans-Peter Störr
la source
Pour info lors de l'utilisation de head: en plaçant '-' devant le nombre avec l'option -n, il imprime toutes les lignes de chaque fichier mais pas les N dernières lignes comme indiqué ci-dessous,
G Koe

Réponses:

38

Si vous avez GNU head, vous pouvez utiliser

head -n -5 file.txt

pour imprimer toutes les lignes sauf les 5 dernières file.txt.

Si head -nne prend aucun argument négatif, essayez

head -n $(( $(wc -l file.txt | awk '{print $1}') - 5 )) file.txt
user570500
la source
11
(et priez pour file.txtau moins six lignes ...)
un CVn
1
Malheureusement, cette version non GNU ne fonctionne pas non plus avec les flux
Armand
1
@ MichaelKjörling Au moins sur Ubuntu, ce n'est pas un problème. Si les fichiers ont moins de lignes que spécifié dans head, une sortie vide est retournée, sans erreur.
Alphaaa
Si je ne me trompe pas, la tête -n 5 affichera les 5 premières lignes, pas toutes sauf les 5 dernières ...
pypmannetjies
8
head file.txt               # first 10 lines
tail file.txt               # last 10 lines
head -n 20 file.txt         # first 20 lines
tail -n 20 file.txt         # last 20 lines
head -20 file.txt           # first 20 lines
tail -20 file.txt           # last 20 lines
head -n -5 file.txt         # all lines except the 5 last
tail -n +5 file.txt         # all lines except the 4 first, starts at line 5
Kjetil S.
la source
1
Qu'est-ce que cet ajout qui n'a pas été répondu dans la réponse acceptée? De même, comme pour vos autres réponses, quelques lignes d'explication de votre réponse l'amélioreraient grandement.
music2myear
1
très bon résumé
ruanhao
5

Voici un moyen simple de supprimer la dernière ligne, qui fonctionne sur BSD, etc.

sed '$d' input.txt

L'expression indique "sur la dernière ligne, supprimez-la". Les autres lignes seront imprimées, car il s'agit seddu comportement par défaut.

Vous pouvez les enchaîner pour supprimer plusieurs lignes

sed '$d' input.txt | sed '$d' | sed '$d'

Ce qui est certes un peu lourd, mais ne fait qu'une seule analyse du fichier.

Vous pouvez également y jeter un œil, pour plus de réponses: https://stackoverflow.com/questions/13380607/how-to-use-sed-to-remove-last-n-lines-of-a-file

Voici un one-liner adapté de l'un de mes favoris là-bas:

N=10
sed -n -e ':a' -e "1,$N!{P;N;D;};N;ba"

Je me suis amusé à déchiffrer celui-ci, et j'espère que vous le ferez aussi (: il fait des Nlignes tampon pendant qu'il scanne, mais sinon c'est assez efficace.

jwd
la source
3

Je suis curieux de savoir pourquoi vous pensez que ce headn'est pas une option:

~$ man head
...
-n, --lines=[-]K
        print the first K lines instead of the first 10; 
        with the leading `-', print all but the last K lines of each file

Cela semble correspondre à votre objectif, en utilisant, par exemple:

head -n -20 yourfile.txt
Anders R. Bystrup
la source
5
Notez que cela ne s'applique qu'à GNU head. BSD headn'a pas cette option, donc cette réponse ne fonctionnera pas sur Solaris ou d'autres Unix sans coreutils GNU. L'OP l'a spécifiquement marqué avec Unix et Unix-Utils.
slhck
2
@slhck Sans parler du fait que l'OP a mentionné que c'est pour Solaris.
un CVn du
Malheureusement, quelqu'un a supprimé ma mention de Solaris. Mais j'aurais quand même dû mentionner que la version de head ne supporte pas ça.
Hans-Peter Störr
1
Désolé tous. Je n'ai pas remarqué Solaris et je n'étais pas au courant des différentes versions de head.
Anders R. Bystrup du
1
@hstoerr Solaris est maintenant dans vos tags :)
slhck
0

Une autre façon de le faire si vous tail -nne prenez pas d'arguments négatifs est

tac file.txt | tail -n +6 | tac

Cela supprimerait les 5 dernières lignes

atw31337
la source
Merci! C'est une idée astucieuse que personne n'a eue jusqu'à présent. Malheureusement, cela serait assez inefficace pour le cas d'utilisation auquel je pensais avec cette question: s'il s'agit d'un gros fichier, il serait non seulement lu complètement une ou plusieurs fois, comme avec les autres solutions, mais il serait probablement également écrit dans disque aux fichiers temporaires par tac s'il ne tient pas en mémoire.
Hans-Peter Störr
@ Hans-Peter Très vrai. Décidé d'écrire un script python3 pour cela. Essayez ceci github.com/atw31337/donkey . Je recommande d'utiliser les options de sortie. Ils fonctionnent beaucoup plus rapidement que l'utilisation de redirections.
atw31337
Joliment écrit! Cependant, il lit le fichier deux fois, ce qui n'est pas vraiment nécessaire si vous mettez en mémoire tampon les n dernières lignes, et c'est un problème sur les gros fichiers. Personnellement, je n'en ai plus besoin, mais au cas où vous vous amuseriez à l'améliorer et que d'autres personnes en auraient besoin ... Il y avait, après tout, quelques likes et signets sur la question.
Hans-Peter Störr
@ Hans-Peter. La taille du tampon dépendrait du nombre de lignes à supprimer. Cela pourrait être un problème si un très grand nombre de lignes devaient être supprimées du fichier. Afin d'éviter les problèmes liés à la mémoire, j'ai réécrit le script pour utiliser la méthode de comptage de lignes avec des valeurs n élevées et une méthode de mise en mémoire tampon avec des valeurs n inférieures; cependant, après l'avoir testé sur un très gros fichier, la méthode de comptage de lignes d'origine est toujours plus rapide. Il semble que la surcharge de gestion de la mémoire tampon l'emporte sur la surcharge de comptage de lignes ... ou je manque juste quelque chose.
atw31337
Bien, mais pour Mac OS X, variante BSD, il n'y a pas de commande tac par défaut. :( Ce genre de défait le cas d'utilisation.
ingyhere