Frente a la cola: todas las líneas excepto las últimas n líneas

36

¿Cómo puedo descartar las últimas n líneas de un archivo con un filtro de línea de comando de Unix?

Eso sería lo contrario de tail: taildescarta las primeras n líneas pero canaliza el resto, pero quiero que el comando canalice todo excepto las últimas n líneas.

Lamentablemente no he encontrado nada de eso, headtampoco ayuda. EDITAR : al menos en Solaris no toma argumentos negativos.

Actualización: estoy interesado principalmente en una solución que funcione para archivos grandes, es decir, archivos de registro, donde es posible que desee inspeccionar lo que sucedió en los últimos minutos.

Hans-Peter Störr
fuente
Para su información al usar la cabeza: Al colocar '-' delante del número con la opción -n, imprime todas las líneas de cada archivo pero no las últimas N líneas como se muestra a continuación,
G Koe

Respuestas:

38

Si tienes GNU head, puedes usar

head -n -5 file.txt

para imprimir todas menos las últimas 5 líneas de file.txt.

Si head -nno toma argumentos negativos, intente

head -n $(( $(wc -l file.txt | awk '{print $1}') - 5 )) file.txt
usuario570500
fuente
11
(y reza file.txtpor lo menos con seis líneas de largo ...)
un CVn el
1
Lamentablemente, esta versión que no es GNU tampoco funciona con transmisiones
Armand el
1
@ MichaelKjörling Al menos en ubuntu, eso no es un problema. Si los archivos tienen menos líneas de las especificadas head, se devuelve una salida vacía, sin errores.
Alphaaa
Si estoy equivocada cabeza no -n 5 imprimirá las primeras 5 líneas, no todos, pero la última ... 5
pypmannetjies
8
head file.txt               # first 10 lines
tail file.txt               # last 10 lines
head -n 20 file.txt         # first 20 lines
tail -n 20 file.txt         # last 20 lines
head -20 file.txt           # first 20 lines
tail -20 file.txt           # last 20 lines
head -n -5 file.txt         # all lines except the 5 last
tail -n +5 file.txt         # all lines except the 4 first, starts at line 5
Kjetil S.
fuente
1
¿Qué agrega esto que no fue respondido en la respuesta aceptada? Además, al igual que con sus otras respuestas, algunas líneas de explicación de su respuesta la mejorarían enormemente.
music2myear
1
muy buen resumen
ruanhao
5

Aquí hay una manera simple de eliminar la última línea, que funciona en BSD, etc.

sed '$d' input.txt

La expresión dice "en la última línea, elimínelo". Las otras líneas se imprimirán, ya que ese es sedel comportamiento predeterminado.

Podrías encadenarlos para eliminar varias líneas

sed '$d' input.txt | sed '$d' | sed '$d'

Lo cual es un poco pesado, es cierto, pero solo escanea el archivo.

También puede consultar esto para obtener más respuestas: https://stackoverflow.com/questions/13380607/how-to-use-sed-to-remove-last-n-lines-of-a-file

Aquí hay una línea adaptada de uno de mis favoritos allí:

N=10
sed -n -e ':a' -e "1,$N!{P;N;D;};N;ba"

Me divertí descifrando ese, y espero que tú también lo hagas (: hace Nlíneas de búfer mientras escanea, pero por lo demás es bastante eficiente.

jwd
fuente
3

Tengo curiosidad por qué crees headque no es una opción:

~$ man head
...
-n, --lines=[-]K
        print the first K lines instead of the first 10; 
        with the leading `-', print all but the last K lines of each file

Esto parece ajustarse a su propósito, usando, por ejemplo:

head -n -20 yourfile.txt
Anders R. Bystrup
fuente
55
Tenga en cuenta que esto solo se aplica a GNU head. BSD headno tiene esta opción, por lo que esta respuesta no funcionará en Solaris u otros Unixes sin GNU coreutils. El OP etiquetó específicamente esto con Unix y Unix-Utils también.
slhck
2
@slhck Sin mencionar el hecho de que el OP mencionó que esto es para Solaris.
un CVn el
Lamentablemente, alguien eliminó mi mención de Solaris. Pero debería haber mencionado de todos modos que la versión de head no es compatible con eso.
Hans-Peter Störr
1
Lo siento todo No me di cuenta de Solaris, ni estaba al tanto de las diversas versiones de head.
Anders R. Bystrup
1
@hstoerr Solaris ahora está en sus etiquetas :)
slhck
0

Otra forma de hacerlo si tail -nno toma argumentos negativos es

tac file.txt | tail -n +6 | tac

Esto eliminaría las últimas 5 líneas.

atw31337
fuente
¡Gracias! Esa es una idea ingeniosa que a nadie se le ocurrió hasta ahora. Desafortunadamente, eso sería bastante ineficiente para el caso de uso que tenía en mente con esta pregunta: si se trata de un archivo grande, no solo se leería por completo una o más veces, como con las otras soluciones, sino que probablemente también se escribiría en disco a archivos temporales por tac si no cabe en la memoria.
Hans-Peter Störr
@ Hans-Peter Muy cierto. Decidí escribir un script python3 para ello. Pruebe este github.com/atw31337/donkey . Recomiendo usar las opciones de salida. Se ejecutan mucho más rápido que el uso de redireccionamientos.
atw31337
Muy bien escrito! Sin embargo, lee el archivo dos veces, lo que no es realmente necesario si almacena las últimas n líneas, y esto es un problema en archivos grandes. Personalmente, ya no lo necesito, pero en caso de que te diviertas mejorándolo y otras personas lo necesiten ... Después de todo, hubo algunos "me gusta" y marcadores en la pregunta.
Hans-Peter Störr
@ Hans-Peter. El tamaño del búfer dependería del número de líneas que se eliminarán. Esto podría ser un problema si fuera necesario eliminar una gran cantidad de líneas del archivo. Para evitar problemas relacionados con la memoria, reescribí el script para usar el método de conteo de líneas con valores altos de n y un método de almacenamiento en búfer con valores n inferiores; sin embargo, después de probarlo en un archivo muy grande, el método de recuento de líneas original sigue siendo más rápido. Parece que la sobrecarga de la gestión del búfer supera la sobrecarga del recuento de líneas ... o simplemente me falta algo.
atw31337
Agradable, pero para la versión BSD de Mac OS X no hay un comando tac por defecto. :( Este tipo de derrotas el caso de uso.
ingyhere