Eliminar la última línea de un archivo en Bash


Tengo un archivo, foo.txt, que contiene las siguientes líneas:

a
b
c

Quiero un comando simple que resulte en que el contenido de foo.txt sea:

a
b
Author: Mateusz Piotrowski, 2011-02-03

11 answers

Utilizando GNU sed:

sed -i '$ d' foo.txt

La opción -i no existe en GNU sed versiones anteriores a la 3.95, por lo que debe usarla como filtro con un archivo temporal:

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

Por supuesto, en ese caso también podría usar head -n -1 en lugar de sed.

 293
Author: thkala,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-01-23 21:43:29

Esta es, con mucho, la solución más rápida y sencilla, especialmente en archivos grandes:

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

Si desea eliminar la línea superior use esto:

tail -n +2 foo.txt

Que significa líneas de salida que comienzan en la línea 2.

No use sed para eliminar líneas de la parte superior o inferior de un archivo it es muy, muy lento si el archivo es grande.

 217
Author: John,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2013-09-22 15:17:29

Tuve problemas con todas las respuestas aquí porque estaba trabajando con un archivo ENORME (~300 Gb) y ninguna de las soluciones escaladas. Esta es mi solución:

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

En palabras: Averigüe la longitud del archivo con la que desea terminar (longitud del archivo menos longitud de la longitud de su última línea, utilizando bc) y, establezca esa posición para que sea el final del archivo (mediante dd un byte de /dev/null en él).

Esto es rápido porque tail comienza a leer desde el final, y dd sobrescribirá el archivo en lugar en lugar de copiar (y analizar) cada línea del archivo, que es lo que hacen las otras soluciones.

NOTA: Esto elimina la línea del archivo en su lugar! Hacer una copia de seguridad o prueba en un archivo ficticio antes de probarlo en su propio archivo!

 95
Author: Yossi Farjoun,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-05-06 19:34:09

Para eliminar la última línea de un archivo sin leer todo el archivo o reescribir nada , puede usar

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

Para eliminar la última línea y también imprimirla en stdout ("pop" it), puede combinar ese comando con tee:

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

Estos comandos pueden procesar eficientemente un archivo muy grande. Esto es similar e inspirado en la respuesta de Yossi, pero evita el uso de algunas funciones adicionales.

Si vas a usar estos repetidamente y quieres manejo de errores y algunas otras características, puede usar el comando poptail aquí: https://github.com/donm/evenmoreutils

 44
Author: ohspite,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-02-27 13:53:49

Usuarios de Mac

Si solo desea eliminar la última línea de salida sin cambiar el archivo en sí, haga

sed -e '$ d' foo.txt

Si desea eliminar la última línea del archivo de entrada, haga

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

 24
Author: manpreet singh,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-02-05 06:26:05

Para Usuarios de Mac :

En Mac, head-n -1 no funcionará. Y, estaba tratando de encontrar una solución simple [sin preocuparme por el tiempo de procesamiento] para resolver este problema solo usando comandos "head" y/o "tail".

Probé la siguiente secuencia de comandos y me alegré de poder resolverlo usando el comando "tail" [con las opciones disponibles en Mac]. Por lo tanto, si está en Mac y desea usar solo "tail" para resolver este problema, puede usar este comando :

Cat file.txt | tail-r | tail-n +2 / tail-r

Explicación:

1 > tail-r: simplemente invierte el orden de las líneas en su entrada

2 > tail-n + 2: esto imprime todas las líneas a partir de la segunda línea en su entrada

 15
Author: Sarfraaz Ahmed,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-10-16 03:37:51
echo -e '$d\nw\nq'| ed foo.txt
 12
Author: lhf,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-01-28 06:03:49
awk 'NR>1{print buf}{buf = $0}'

Esencialmente, este código dice lo siguiente:

Para cada línea después de la primera, imprima la línea tamponada

Para cada línea, restablezca el búfer

El búfer está desfasado por una línea, por lo tanto, termina imprimiendo líneas 1 a n-1

 8
Author: Foo Bah,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2011-02-03 01:47:44
awk "NR != `wc -l < text.file`" text.file |> text.file

Este fragmento hace el truco.

 0
Author: Michael Kingski,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-10-13 15:09:53

Ambas soluciones están aquí en otras formas. Encontré esto un poco más práctico, claro y útil:

Usando 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}

Usando truncate:

BADLINESCOUNT=1
ORIGINALFILE=/tmp/whatever
truncate -s $(printf "$(stat --format=%s ${ORIGINALFILE}) - $(tail -n${BADLINESCOUNT} ${ORIGINALFILE} | wc -c)\n" | bc ) ${ORIGINALFILE}
 0
Author: virtual-light,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-11-16 16:27:06

Ruby(1.9+)

ruby -ne 'BEGIN{prv=""};print prv ; prv=$_;' file
 -3
Author: kurumi,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2011-02-03 02:43:24