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

332

Tengo un archivo que foo.txtcontiene las siguientes líneas:

a
b
c

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

a
b
Fragsworth
fuente

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
thkala
fuente
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.

Juan
fuente
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!

Yossi Farjoun
fuente
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

ohspite
fuente
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

manpreet singh
fuente
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

Sarfraaz Ahmed
fuente
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
lhf
fuente
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

Foo Bah
fuente
2
Esta solución es un filtro y no edita el archivo en su lugar.
lhf
0
awk "NR != `wc -l < text.file`" text.file |> text.file

Este fragmento hace el truco.

Michael Kingski
fuente
Esto simplemente borra todo el archivo para mí.
Kartik Chugh
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}
luz virtual
fuente
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
jasonleonhard
fuente
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.

Michael Rybkin
fuente
2
El énfasis en la simplicidad se perdió aquí
Peter M
-4

Rubí (1.9+)

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