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


Respuestas:


408

Utilizando GNU sed:

sed -i '$ d' foo.txt

La -iopción no existe en GNU sedversiones anteriores a 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 en head -n -1lugar de sed.

Mac OS:

En Mac OS X (a partir de 10.7.4), el equivalente del sed -icomando anterior es

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

56
En Mac OS X (a partir de 10.7.4), el equivalente del sed -icomando anterior es sed -i '' -e '$ d' foo.txt. Además, head -n -1actualmente no funcionará en una Mac.
mklement0

26
¿Podría explicar qué hace '$ d' regex? Se trata de eliminar la última línea, así que creo que esta es la parte más importante para todos los que ven esta pregunta. Gracias :)
kravemir

38
@Miro: De ninguna manera es $ duna expresión regular. Es un comando sed. des el comando para eliminar una línea, mientras que $significa "la última línea del archivo". Al especificar una ubicación (llamada "rango" en la jerga sed) antes de un comando, ese comando solo se aplica a la ubicación especificada. Entonces, este comando dice explícitamente "en el rango de la última línea de un archivo, elimínelo". Muy hábil y directo al grano, si me preguntas.
Daniel Kamil Kozar

1
¿Cómo eliminas la última línea pero solo si es una línea vacía?
skan

1
@Alex: actualizado para GNU sed; no
tengo

279

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

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

No lo use sedpara eliminar líneas de la parte superior o inferior de un archivo; es muy lento si el archivo es grande.


16
head -n -1 foo.txtes suficiente
ДМИТРИЙ МАЛИКОВ

20
head -n -1 no funciona en la cabeza de bdsutils. al menos no en la versión que usa macos, por lo que esto no funciona.
johannes_lalala

23
@johannes_lalala En Mac, puede brew install coreutils(utilidades principales de GNU) y usar en gheadlugar de head.
interestinglythere

Aquí hay un script bash simple que lo automatiza en caso de que tenga varios archivos con diferentes números de líneas en la parte inferior para eliminar: cat TAILfixer FILE=$1; TAIL=$2; head -n -${TAIL} ${FILE} > temp ; mv temp ${FILE}Ejecútelo para eliminar 4 líneas, por ejemplo, de myfile como: ./TAILfixer myfile 4por supuesto, primero chmod +x TAILfixer
hágalo

Pulgares arriba porque funcionó, pero a pesar de la comparación sed, este comando también es bastante lento
Jeff Diederiks

121

Tuve problemas con todas las respuestas aquí porque estaba trabajando con un archivo ENORME (~ 300Gb) y ninguna de las soluciones se ajustó. Aquí está 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: descubra la longitud del archivo con el que desea terminar (longitud del archivo menos la longitud de su última línea, usando bc) y configure esa posición para que sea el final del archivo ( ddingresando un byte de /dev/null a eso).

Esto es rápido porque tailcomienza a leer desde el final y ddsobrescribirá el archivo en su 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! ¡Haga una copia de seguridad o pruebe en un archivo ficticio antes de probarlo en su propio archivo!


3
Ni siquiera sabía sobre dd. Esta debería ser la mejor respuesta. Muchas gracias! Es una pena que la solución no funcione para (bloquear) archivos comprimidos.
tommy.carstensen

44
Enfoque muy, muy inteligente! Solo una nota: requiere y opera en un archivo real, por lo que no se puede usar en tuberías, sustituciones de comandos, redirecciones y tales cadenas.
MestreLion

3
Esta debería ser la mejor respuesta. Trabajó instantáneamente para mi archivo de ~ 8 GB.
Peter Cogan

8
Usuarios de mac: dd if = / dev / null of = <filename> bs = 1 seek = $ (echo $ (stat -f =% z <filename> | cut -c 2-) - $ (tail -n1 <filename> | wc -c) | bc)
Igor

2
¡Este enfoque es el camino a seguir para archivos grandes!
thomas.han

61

Para eliminar la última línea de un archivo sin leer el archivo completo 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"), 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 por la respuesta de Yossi, pero evita el uso de algunas funciones adicionales.

Si va a usarlos repetidamente y desea manejar errores y algunas otras funciones, puede usar el poptailcomando aquí: https://github.com/donm/evenmoreutils


3
Nota rápida: truncateno está disponible en OS X por defecto. Pero es fácil de instalar con Brew: brew install truncate.
Guillaume Boudreau

3
Muy agradable. Mucho mejor sin dd. Estaba aterrorizado de usar dd como un solo dígito o letra mal
ubicado que

1
(ADVERTENCIA DE Broma) En realidad, ("dd" -> "desastre") requiere 1 sustitución y 6 inserciones; apenas "un solo dígito o letra fuera de lugar". :)
Kyle

29

Usuarios de Mac

si solo desea que la última línea elimine la salida sin cambiar el archivo, haga

sed -e '$ d' foo.txt

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

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


Esto funciona para mí pero no lo entiendo. de todos modos, funcionó.
Melvin

La bandera -i ''le dice a sed que modifique el archivo en el lugar, mientras mantiene una copia de seguridad en un archivo con la extensión provista como parámetro. Como el parámetro es una cadena vacía, no se crea ningún archivo de respaldo. -ele dice a sed que ejecute un comando. El comando $ dsignifica: buscar la última línea ( $) y eliminarla ( d).
Krzysztof Szafranek

24

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 los comandos "head" y / o "tail".

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

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

Explicacion:

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

2> tail -n +2: imprime todas las líneas que comienzan desde la segunda línea en su entrada


2
Instalar coreutils usando Homebrew le dará la cabeza de GNU como 'ghead', lo que le permitirá hacerloghead -n -1
algo así el

13
echo -e '$d\nw\nq'| ed foo.txt

2
Para una solución de filtro que no edite el archivo en su lugar, use sed '$d'.
lhf

8
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 almacenada

para cada línea, reinicie el búfer

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


2
Esta solución es un filtro y no edita el archivo en su lugar.
lhf


0

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 truncar:

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

Ay. Demasiado complicado.
Robert

0

Linux

$ es la última línea, d para eliminar:

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

Mac OS

Equivalente de sed -i

sed -i '' -e '$ d' ~/path/to/your/file/name

0

Así es como puede hacerlo manualmente (yo personalmente uso mucho este método cuando necesito eliminar rápidamente la última línea de un archivo):

vim + [FILE]

Ese + signo allí significa que cuando el archivo se abre en el editor de texto vim, el cursor se colocará en la última línea del archivo.

Ahora solo presione ddos veces en su teclado. Esto hará exactamente lo que desea: eliminar la última línea. Después de eso, presione :seguido de xy luego presione Enter. Esto guardará los cambios y lo llevará de regreso al shell. Ahora, la última línea se ha eliminado con éxito.


2
El énfasis en la simplicidad se perdió aquí
Peter M

-4

Rubí (1.9+)

ruby -ne 'BEGIN{prv=""};print prv ; prv=$_;' file
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.