Supprimer la dernière ligne d'un fichier dans Bash

332

J'ai un fichier foo.txt, contenant les lignes suivantes:

a
b
c

Je veux une commande simple qui se traduit par le contenu de foo.txt:

a
b
Fragsworth
la source

Réponses:

408

En utilisant GNU sed:

sed -i '$ d' foo.txt

L' -ioption n'existe pas dans les GNU sedversions antérieures à 3,95, vous devez donc l'utiliser comme filtre avec un fichier temporaire:

cp foo.txt foo.txt.tmp
sed '$ d' foo.txt.tmp > foo.txt
rm -f foo.txt.tmp

Bien sûr, dans ce cas, vous pouvez également utiliser à la head -n -1place de sed.

MacOS:

Sur Mac OS X (à partir de 10.7.4), l'équivalent de la sed -icommande ci-dessus est

sed -i '' -e '$ d' foo.txt
thkala
la source
56
Sur Mac OS X (à partir de 10.7.4), l'équivalent de la sed -icommande ci-dessus est sed -i '' -e '$ d' foo.txt. De plus, head -n -1ne fonctionnera pas actuellement sur un Mac.
mklement0
26
Pourriez-vous expliquer ce que fait «$ d» regex? C'est la question de supprimer la dernière ligne, donc je pense que c'est la partie la plus importante pour tous ceux qui consultent cette question. Merci :)
kravemir
38
@Miro: Ce n'est en aucun cas $ dune expression régulière. C'est une commande sed. dest la commande pour supprimer une ligne, tandis que $signifie "la dernière ligne du fichier". Lorsque vous spécifiez un emplacement (appelé "plage" dans le jargon sed) avant une commande, cette commande n'est appliquée qu'à l'emplacement spécifié. Ainsi, cette commande dit explicitement "dans la plage de la dernière ligne d'un fichier, supprimez-le". Assez lisse et droit au but, si vous me demandez.
Daniel Kamil Kozar
1
Comment supprimez-vous la dernière ligne mais uniquement s'il s'agit d'une ligne vide?
skan
1
@Alex: mis à jour pour GNU sed; aucune idée sur les différentes versions de BSD / UNIX / MacOS sed ...
thkala
279

C'est de loin la solution la plus rapide et la plus simple, notamment sur les gros fichiers:

head -n -1 foo.txt > temp.txt ; mv temp.txt foo.txt

si vous souhaitez supprimer la ligne supérieure, utilisez ceci:

tail -n +2 foo.txt

ce qui signifie que les lignes de sortie commencent à la ligne 2.

Ne pas utiliser sedpour supprimer des lignes du haut ou du bas d'un fichier - c'est très très lent si le fichier est volumineux.

John
la source
16
head -n -1 foo.txtsuffit
ДМИТРИЙ МАЛИКОВ
20
head -n -1 ne fonctionne pas sur la tête de bdsutils. du moins pas dans la version que macos utilise, donc cela ne fonctionne pas.
johannes_lalala
23
@johannes_lalala Sur Mac, vous pouvez brew install coreutils(utilitaires principaux GNU) et utiliser à la gheadplace de head.
intéressant de noter qu'il y a
Voici un script bash simple qui l'automatise au cas où vous auriez plusieurs fichiers avec différents nombres de lignes en bas à supprimer: cat TAILfixer FILE=$1; TAIL=$2; head -n -${TAIL} ${FILE} > temp ; mv temp ${FILE}Exécutez-le pour supprimer 4 lignes par exemple de mon fichier comme: ./TAILfixer myfile 4bien sûr, rendez-le d'abord exécutable parchmod +x TAILfixer
FatihSarigol
Bravo parce que cela a fonctionné, mais pour toute la comparaison avec sed, cette commande est aussi sacrément lente
Jeff Diederiks
121

J'ai eu des problèmes avec toutes les réponses ici parce que je travaillais avec un fichier ÉNORME (~ 300 Go) et aucune des solutions n'était mise à l'échelle. Voici ma solution:

dd if=/dev/null of=<filename> bs=1 seek=$(echo $(stat --format=%s <filename> ) - $( tail -n1 <filename> | wc -c) | bc )

En d'autres termes: déterminez la longueur du fichier avec bclequel vous souhaitez vous retrouver (longueur du fichier moins longueur de la dernière ligne, à l'aide de ) et définissez cette position comme étant la fin du fichier (en ddingérant un octet de /dev/nullsur il).

Ceci est rapide car tailcommence la lecture depuis la fin et ddécrase le fichier en place plutôt que de copier (et d'analyser) chaque ligne du fichier, ce que font les autres solutions.

REMARQUE: cela supprime la ligne du fichier en place! Faites une sauvegarde ou un test sur un fichier factice avant de l'essayer sur votre propre fichier!

Yossi Farjoun
la source
3
Je ne connaissais même pas dd. Cela devrait être la meilleure réponse. Merci beaucoup! C'est dommage que la solution ne fonctionne pas pour (bloquer) les fichiers compressés.
tommy.carstensen
4
Approche très, très intelligente! Juste une note: il nécessite et fonctionne sur un vrai fichier, il ne peut donc pas être utilisé sur des pipes, des substitutions de commandes, des redirections et de telles chaînes.
MestreLion
3
Cela devrait être la meilleure réponse. Fonctionné instantanément pour mon fichier ~ 8 Go.
Peter Cogan
8
utilisateurs Mac: dd if = / dev / null of = <nomfichier> bs = 1 cherche = $ (echo $ (stat -f =% z <nomfichier> | cut -c 2-) - $ (tail -n1 <nomfichier> | wc -c) | bc)
Igor
2
Cette approche est la voie à suivre pour les gros fichiers!
thomas.han
61

Pour supprimer la dernière ligne d'un fichier sans lire l'intégralité du fichier ni réécrire quoi que ce soit , vous pouvez utiliser

tail -n 1 "$file" | wc -c | xargs -I {} truncate "$file" -s -{}

Pour supprimer la dernière ligne et également l'imprimer sur stdout ("pop" it), vous pouvez combiner cette commande avec tee:

tail -n 1 "$file" | tee >(wc -c | xargs -I {} truncate "$file" -s -{})

Ces commandes peuvent traiter efficacement un très gros fichier. Ceci est similaire et inspiré par la réponse de Yossi, mais cela évite d'utiliser quelques fonctions supplémentaires.

Si vous allez les utiliser à plusieurs reprises et que vous souhaitez gérer les erreurs et d'autres fonctionnalités, vous pouvez utiliser la poptailcommande ici: https://github.com/donm/evenmoreutils

ohspite
la source
3
Remarque rapide: truncaten'est pas disponible sur OS X par défaut. Mais il est facile à installer à l' aide Brew: brew install truncate.
Guillaume Boudreau
3
Très agréable. Beaucoup mieux sans dd. J'étais terrifiée à l'idée d'utiliser dd comme un seul chiffre ou lettre égaré peut signifier un désastre. +1
Yossi Farjoun
1
(AVERTISSEMENT DE JOKE) En fait, ("jj" -> "catastrophe") nécessite 1 substitution et 6 insertions; à peine "un seul chiffre ou lettre égaré". :)
Kyle
29

Utilisateurs Mac

si vous voulez seulement que la dernière ligne soit supprimée sans changer le fichier lui-même

sed -e '$ d' foo.txt

si vous souhaitez supprimer la dernière ligne du fichier d'entrée

sed -i '' -e '$ d' foo.txt

manpreet singh
la source
Cela fonctionne pour moi mais je ne comprends pas. de toute façon, cela a fonctionné.
Melvin
L'indicateur -i ''indique à sed de modifier le fichier sur place, tout en conservant une sauvegarde dans un fichier avec l'extension fournie en paramètre. Le paramètre étant une chaîne vide, aucun fichier de sauvegarde n'est créé. -eindique à sed d'exécuter une commande. La commande $ dsignifie: trouver la dernière ligne ( $) et la supprimer ( d).
Krzysztof Szafranek
24

Pour les utilisateurs Mac:

Sur Mac, head -n -1 ne fonctionnera pas. Et, j'essayais de trouver une solution simple [sans se soucier du temps de traitement] pour résoudre ce problème uniquement en utilisant les commandes "head" et / ou "tail".

J'ai essayé la séquence de commandes suivante et j'étais heureux de pouvoir le résoudre en utilisant simplement la commande "tail" [avec les options disponibles sur Mac]. Donc, si vous êtes sur Mac et que vous souhaitez utiliser uniquement "tail" pour résoudre ce problème, vous pouvez utiliser cette commande:

cat file.txt | queue -r | queue -n +2 | queue -r

Explication:

1> tail -r: inverse simplement l'ordre des lignes dans son entrée

2> tail -n +2: cela imprime toutes les lignes à partir de la deuxième ligne dans son entrée

Sarfraaz Ahmed
la source
2
L'installation de coreutils en utilisant Homebrew vous donnera la tête GNU en tant que 'ghead', vous permettant de le faireghead -n -1
quelque peu
13
echo -e '$d\nw\nq'| ed foo.txt
lhf
la source
2
Pour une solution de filtrage qui ne modifie pas le fichier en place, utilisez sed '$d'.
lhf
8
awk 'NR>1{print buf}{buf = $0}'

Essentiellement, ce code dit ce qui suit:

Pour chaque ligne après la première, imprimez la ligne en mémoire tampon

pour chaque ligne, réinitialisez le tampon

Le tampon est décalé d'une ligne, vous finissez donc par imprimer les lignes 1 à n-1

Foo Bah
la source
2
Cette solution est un filtre et ne modifie pas le fichier en place.
lhf
0
awk "NR != `wc -l < text.file`" text.file |> text.file

Cet extrait fait l'affaire.

Michael Kingski
la source
Cela efface tout le fichier pour moi.
Kartik Chugh
0

Ces deux solutions sont ici sous d'autres formes. Je les ai trouvées un peu plus pratiques, claires et utiles:

Utilisation de dd:

BADLINESCOUNT=1
ORIGINALFILE=/tmp/whatever
dd if=${ORIGINALFILE} of=${ORIGINALFILE}.tmp status=none bs=1 count=$(printf "$(stat --format=%s ${ORIGINALFILE}) - $(tail -n${BADLINESCOUNT} ${ORIGINALFILE} | wc -c)\n" | bc )
/bin/mv -f ${ORIGINALFILE}.tmp ${ORIGINALFILE}

Utilisation de tronquer:

BADLINESCOUNT=1
ORIGINALFILE=/tmp/whatever
truncate -s $(printf "$(stat --format=%s ${ORIGINALFILE}) - $(tail -n${BADLINESCOUNT} ${ORIGINALFILE} | wc -c)\n" | bc ) ${ORIGINALFILE}
lumière virtuelle
la source
Aie. Beaucoup trop compliqué.
Robert
0

Linux

$ est la dernière ligne, d pour supprimer:

sed '$d' ~/path/to/your/file/name

MacOS

Équivalent du sed -i

sed -i '' -e '$ d' ~/path/to/your/file/name
jasonleonhard
la source
0

Voici comment vous pouvez le faire manuellement (j'utilise personnellement cette méthode beaucoup lorsque j'ai besoin de supprimer rapidement la dernière ligne d'un fichier):

vim + [FILE]

Ce +signe signifie que lorsque le fichier est ouvert dans l'éditeur de texte vim, le curseur sera positionné sur la dernière ligne du fichier.

Maintenant, appuyez simplement ddeux fois sur votre clavier. Cela fera exactement ce que vous voulez: supprimez la dernière ligne. Après cela, appuyez :sur x, puis sur Enter. Cela enregistrera les modifications et vous ramènera au shell. Maintenant, la dernière ligne a été supprimée avec succès.

Michael Rybkin
la source
2
L'accent sur la simplicité a été perdu ici
Peter M
-4

Rubis (1.9+)

ruby -ne 'BEGIN{prv=""};print prv ; prv=$_;' file
kurumi
la source