Cómo usar sed para eliminar las últimas n líneas de un archivo

148

Quiero eliminar algunos n líneas del final de un archivo. ¿Se puede hacer esto usando sed?

Por ejemplo, para eliminar líneas de 2 a 4, puedo usar

$ sed '2,4d' file

Pero no sé los números de línea. Puedo eliminar la última línea usando

$sed $d file

pero quiero saber la forma de eliminar n líneas del final. Por favor, hágame saber cómo hacerlo usando sed o algún otro método.

mezda
fuente
2
@arashkordi: que utiliza un recuento de líneas desde la parte superior del archivo, no desde la parte inferior.
enms.
Pregunta relacionada sobre superusuarios .
Thor
// ¿Quizás consideres reformular la pregunta para hacerla más general que simplemente sed ?
Nathan Basanese
1
sed $d filedevuelve un error En cambio, $ d debería estar entre comillas, así:sed '$d' file
Akronix

Respuestas:

221

No lo sé sed, pero se puede hacer con head:

head -n -2 myfile.txt
ams
fuente
19
+1 por simplicidad. El comando sed equivalente es feo: sed -e :a -e '$d;N;2,5ba' -e 'P;D' file( ,5para las últimas 5 líneas).
Marc B
62
Tenga en cuenta que esto funciona con algunas versiones de head, pero no es estándar. De hecho, el estándar para los headestados:The application shall ensure that the number option-argument is a positive decimal integer.
William Pursell
21
Para agregar a la respuesta de @ WilliamPursell ... En el shell predeterminado de Mac OS, esto no funciona.
JDS
77
Tampoco en BSD, pero la pregunta está etiquetada Linux.
enms.
3
menos 1, porque, además de osx, esto no funciona para mí en Ubuntu14 -head: illegal line count -- -2
Michael Durrant
30

Si la opción de codificación n es una opción, puede usar llamadas secuenciales para sed. Por ejemplo, para eliminar las últimas tres líneas, elimine la última línea tres veces:

sed '$d' file | sed '$d' | sed '$d'
Kyle
fuente
2
Esto, más -i para editar el archivo en su lugar en lugar de volcarlo en stdout, funcionó para mí.
WAF
27

De los one-liners sed :

# delete the last 10 lines of a file
sed -e :a -e '$d;N;2,10ba' -e 'P;D'   # method 1
sed -n -e :a -e '1,10!{P;N;D;};N;ba'  # method 2

Parece ser lo que estás buscando.

qstebom
fuente
@Thor puede cambiar el número de líneas se elimina del archivo cambiando la 10en'$d;N;2,10ba'
Alexej Magura
1
@AlexejMagura: Estaba comentando una versión anterior de la respuesta.
Thor
22

Una divertida y sencilla sedy la tacsolución:

n=4
tac file.txt | sed "1,$n{d}" | tac

NOTA

  • "se necesitan comillas dobles para que el shell evalúe la $nvariable al sedmando. En comillas simples, no se realizará ninguna interpolación.
  • taces un catreverso, verman 1 tac
  • los {}in sedestán ahí para separar $n& d(si no, el shell intenta interpolar $ndvariables no existentes )
Gilles Quenot
fuente
77
tacdigo no en osx
Michael Durrant
1
@MichaelDurrant port info coreutils|| brew info coreutils;
voces
19

Use sed, pero deje que el shell haga los cálculos, con el objetivo de usar el dcomando dando un rango (para eliminar las últimas 23 líneas):

sed -i "$(($(wc -l < file)-22)),\$d" file

Para eliminar las últimas 3 líneas, de adentro hacia afuera:

$(wc -l < file)

Da el número de líneas del archivo: digamos 2196

Queremos eliminar las últimas 23 líneas, así que para el lado izquierdo o rango:

$((2196-22))

Da: 2174 Así, la interpretación original de sed después de la concha es:

sed -i '2174,$d' file

¡Con la -iedición in situ, el archivo ahora tiene 2173 líneas!

Si desea guardarlo en un nuevo archivo, el código es:

sed -i '2174,$d' file > outputfile
lzc
fuente
15

Podrías usar cabeza para esto.

Utilizar

$ head --lines=-N file > new_file

donde N es el número de líneas que desea eliminar del archivo.

Los contenidos del archivo original menos las últimas N líneas están ahora en new_file

Ricardo
fuente
8

Solo para completar, me gustaría agregar mi solución. Terminé haciendo esto con el estándar ed:

ed -s sometextfile <<< $'-2,$d\nwq'

Esto elimina las últimas 2 líneas usando la edición en el lugar (¡aunque usa un archivo temporal en /tmp!)

Michel
fuente
5

Para truncar archivos muy grandes realmente en su lugar, tenemos un truncatecomando. No conoce las líneas, pero tail+ wcpuede convertir líneas a bytes:

file=bigone.log
lines=3
truncate -s -$(tail -$lines $file | wc -c) $file

Hay una condición de carrera obvia si el archivo se escribe al mismo tiempo. En este caso, puede ser mejor usarlo head: cuenta los bytes desde el comienzo del archivo (mind disk IO), por lo que siempre truncaremos en el límite de la línea (posiblemente más líneas de las esperadas si el archivo se escribe activamente):

truncate -s $(head -n -$lines $file | wc -c) $file

Práctico one-liner si falla el intento de inicio de sesión poniendo la contraseña en lugar del nombre de usuario:

truncate -s $(head -n -5 /var/log/secure | wc -c) /var/log/secure
Kamil Christ
fuente
4

Esto podría funcionar para usted (GNU sed):

sed ':a;$!N;1,4ba;P;$d;D' file
potong
fuente
Agradable. Te perdiste el apóstrofe final (' ).
Thor
lo siento por el comentario tan tardío pero lo intento y (adaptado para mi AIX / posix) falló la impresión de todas menos la última línea. Al leer el código, no entiendo cómo se eliminan las últimas cuatro líneas, el bucle explícito está en 4 primeras líneas, por lo que ciertamente se repite después de a d, Do Pcon GNU sed y no en la versión posix. Parece que las líneas no se imprimen después de Dy se mantienen en el búfer de trabajo para un nuevo bucle sin pasar al final de la línea de acciones.
NeronLeVelu
@NeronLeVelu el programa lee en una ventana de 4 líneas en el espacio del patrón y luego agrega la siguiente línea e imprime la primera hasta que llega al final del archivo donde elimina las líneas restantes.
potong
@potong sí, pruebo en un sed de GNU y mantiene el búfer entre líneas donde posix lo descarga después del D hacer el efecto contrario.
NeronLeVelu
4

La mayoría de las respuestas anteriores parecen requerir comandos / extensiones de GNU:

    $ head -n -2 myfile.txt
    -2: Badly formed number

Para una solución un poco más portátil:

     perl -ne 'push(@fifo,$_);print shift(@fifo) if @fifo > 10;'

O

     perl -ne 'push(@buf,$_);END{print @buf[0 ... $#buf-10]}'

O

     awk '{buf[NR-1]=$0;}END{ for ( i=0; i < (NR-10); i++){ print buf[i];} }'

Donde "10" es "n".

Corazón oscuro
fuente
2

Con las respuestas aquí, ya habrás aprendido que sed no es la mejor herramienta para esta aplicación.

Sin embargo, creo que hay una manera de hacer esto al usar sed; la idea es agregar N líneas para mantener el espacio hasta que pueda leer sin presionar EOF. Cuando se golpea EOF, imprima el contenido del espacio de espera y salga.

sed -e '$!{N;N;N;N;N;N;H;}' -e x

El comando sed anterior omitirá las últimas 5 líneas.

Rishabh Sagar
fuente
2

Pruebe el siguiente comando:

n = line number
tail -r file_name | sed '1,nd' | tail -r
Rishav Kaushal
fuente
Por favor explique cómo funciona esto. FYI: para inicializar las variables de shell, no deberíamos tener un espacio alrededor =.
codeforester
@codeforester La primera línea fue un comentario, supongo. Simplemente usa tail -rdonde otros han usado tacpara invertir el orden de las líneas y hacer que las últimas nlíneas se conviertan en las nprimeras.
Nicolas Melay
2

Se puede hacer en 3 pasos:

a) Cuente el número de líneas en el archivo que desea editar:

 n=`cat myfile |wc -l`

b) Reste de ese número el número de líneas a eliminar:

 x=$((n-3))

c) Indique a sed que elimine de ese número de línea ( $x) hasta el final:

 sed "$x,\$d" myfile
Rey Lefebvre
fuente
Matemáticas malas Si debe eliminar 3 líneas, reste 3 pero AGREGUE 1. Con sus cálculos, si el archivo tiene 10 líneas, 10 - 3 = 7 y está utilizando sedpara eliminar las líneas 7 a 10, es decir 4 líneas, no 3.
mathguy
1

Puede obtener el recuento total de líneas con wc -l <file>y usar

head -n <total lines - lines to remove> <file>

SWW13
fuente
1

Esto eliminará las últimas 3 líneas de file:

for i in $(seq 1 3); do sed -i '$d' file; done;

usuario5780554
fuente
1
Esto es demasiado costoso para archivos grandes: ¡todo el archivo debe leerse y reescribirse n veces! Y tantos tenedores para sed.
codeforester
Si bien esta puede no ser la solución más eficiente, es, con mucho, la más sencilla y fácil de entender
Dharam
0

Prefiero esta solución

head -$(gcalctool -s $(cat file | wc -l)-N) file

donde N es el número de líneas para eliminar.

p014k
fuente
0
sed -n ':pre
1,4 {N;b pre
    }
:cycle
$!{P;N;D;b cycle
  }' YourFile

versión posix

NeronLeVelu
fuente
0

Para eliminar las últimas 4 líneas:

$ nl -b a file | sort -k1,1nr | sed '1, 4 d' | sort -k1,1n | sed 's/^ *[0-9]*\t//'   
mstafreshi
fuente
un poco pesado (especialmente en recursos) en comparación con una cabeza. Al menos tac file | sed '1,4d' | tacporque ordenar y luego eliminar el prefijo cuesta muchos recursos.
NeronLeVelu
@NeronLeVelu tiene razón, pero no hay ningún taccomando en algunos sistemas (por ejemplo, FreeBSD?)
mstafreshi
0

Se me ocurrió esto, donde n es el número de líneas que desea eliminar:

count=`wc -l file`
lines=`expr "$count" - n`
head -n "$lines" file > temp.txt
mv temp.txt file
rm -f temp.txt

Es una pequeña rotonda, pero creo que es fácil de seguir.

  1. Cuente el número de líneas en el archivo principal
  2. Reste el número de líneas que desea eliminar del recuento
  3. Imprima la cantidad de líneas que desea conservar y almacene en un archivo temporal
  4. Reemplace el archivo principal con el archivo temporal
  5. Eliminar el archivo temporal
coyotetechy
fuente
0

Para eliminar las últimas N líneas de un archivo, puede usar el mismo concepto de

$ sed '2,4d' file

Puede usar un combo con comando de cola para revertir el archivo: si N es 5

$ tail -r file | sed '1,5d' file | tail -r > file

Y de esta manera también se ejecuta donde el head -n -5 filecomando no se ejecuta (¡como en una Mac!).

Alessandra Bilardi
fuente
-2

Esto eliminará las últimas 12 líneas.

sed -n -e :a -e '1,10!{P;N;D;};N;ba'
R. Kumar
fuente
1
Un poco de explicación ayudaría por favor.
Richard Wiseman