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

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

 a b c 

Je veux une commande simple qui aboutit au contenu de foo.txt :

 a b 

    Utiliser GNU sed :

     sed -i '$ d' foo.txt 

    L’option -i n’existe pas dans les versions GNU sed 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 head -n -1 au lieu de sed .

    C’est de loin la solution la plus rapide et la plus simple, en particulier sur les gros fichiers:

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

    Si vous voulez supprimer la première ligne, utilisez ceci:

     tail -n +2 foo.txt 

    ce qui signifie des lignes de sortie commençant à la ligne 2.

    N’utilisez pas sed pour supprimer des lignes du haut ou du bas d’un fichier – c’est très très lent si le fichier est volumineux.

    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’a été mise à l’échelle. Voici ma solution:

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

    En mots: Trouvez la longueur du fichier que vous voulez obtenir (longueur du fichier moins longueur de la dernière ligne, en utilisant bc ) et définissez cette position comme la fin du fichier (en dd un octet) de /dev/null dessus.

    C’est rapide car tail commence à lire à partir de la fin et dd remplace le fichier en place plutôt que de copier (et d’parsingr) 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!

    Pour supprimer la dernière ligne d’un fichier sans lire tout le fichier ou 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 l’imprimer également sur stdout (“pop”), 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 souhaitez les utiliser de manière répétée et que vous souhaitez gérer les erreurs et certaines autres fonctionnalités, vous pouvez utiliser la commande poptail ici: https://github.com/donm/evenmoreutils

    Utilisateurs Mac

    si vous ne voulez que la dernière ligne de sortie supprimée sans changer le fichier lui-même faire

    sed -e '$ d' foo.txt

    si vous voulez supprimer la dernière ligne du fichier d’entrée lui-même faire

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

    Pour les utilisateurs Mac:

    Sur Mac, la tête -n -1 ne fonctionnera pas. Et, j’essayais de trouver une solution simple [sans me soucier du temps de traitement] pour résoudre ce problème en utilisant uniquement 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 voulez utiliser uniquement “tail” pour résoudre ce problème, vous pouvez utiliser cette commande:

    fichier cat.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: affiche toutes les lignes à partir de la deuxième ligne de son entrée

     echo -e '$d\nw\nq'| ed foo.txt 
     awk 'NR>1{print buf}{buf = $0}' 

    Essentiellement, ce code dit ceci:

    Pour chaque ligne après le premier, imprimez la ligne en mémoire tampon

    pour chaque ligne, réinitialiser le tampon

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

     awk "NR != `wc -l < text.file`" text.file |> text.file 

    Cet extrait fait l’affaire.

    Ces deux solutions sont ici sous d’autres formes. Je les ai trouvés un peu plus pratiques, clairs et utiles:

    En utilisant 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} 

    En utilisant tronquer:

     BADLINESCOUNT=1 ORIGINALFILE=/tmp/whatever truncate -s $(printf "$(stat --format=%s ${ORIGINALFILE}) - $(tail -n${BADLINESCOUNT} ${ORIGINALFILE} | wc -c)\n" | bc ) ${ORIGINALFILE} 

    Rubis (1.9+)

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