¿Cómo mantiene solo las últimas n líneas de un archivo de registro?

18

Un script que escribí hace algo y, al final, agrega algunas líneas a su propio archivo de registro. Me gustaría mantener solo las últimas n líneas (digamos, 1000 líneas) del archivo de registro. Esto se puede hacer al final del script de esta manera:

tail -n 1000 myscript.log > myscript.log.tmp
mv -f myscript.log.tmp myscript.log

pero hay una solución más limpia y elegante? Tal vez logrado a través de un solo comando?

dr01
fuente
logrotatees la solución elegante
Ipor Sircer
1
Lo he pensado, pero la configuración de logrotate sería más larga que la secuencia de comandos en sí ...
dr01
Si logrotate es excesivo, su solución es tan elegante como parece. Con sed / awk, puede hacerlo en una línea pero no sin un archivo temporal internamente, por lo que probablemente no sea más eficiente y probablemente menos legible.
kba está con Monica el

Respuestas:

28

Es posible así, pero como han dicho otros, la opción más segura es la generación de un nuevo archivo y luego un movimiento de ese archivo para sobrescribir el original.

El siguiente método carga las líneas en BASH, por lo que, dependiendo del número de líneas desde tail, eso afectará el uso de memoria del shell local para almacenar el contenido de las líneas de registro.

El siguiente también elimina las líneas vacías en caso de que existan al final del archivo de registro (debido al comportamiento de la evaluación de BASH "$(tail -1000 test.log)"), por lo que no proporciona un truncamiento verdaderamente 100% preciso en todos los escenarios, pero dependiendo de su situación, puede ser suficiente.

$ wc -l myscript.log
475494 myscript.log

$ echo "$(tail -1000 myscript.log)" > myscript.log

$ wc -l myscript.log
1000 myscript.log
parkamark
fuente
Inteligente. Marqué esto como la respuesta aceptada, ya que no requiere la instalación de herramientas adicionales. Desearía poder aceptar tanto la suya como la respuesta de @ John1024.
dr01
Tu llamada. He votado a favor de la solución de esponja porque no lo sabía y está garantizado que no se meterá con líneas de registro vacías. Esta solución tiene el potencial de hacerlo, dependiendo del contenido del archivo de registro.
parkamark
Esta solución tiene una condición de carrera. Si no tiene suerte, la redirección -en- el archivo ocurre antes de la lectura -de- el archivo y termina con un archivo vacío.
Coroos hace
21

La utilidad spongeestá diseñada solo para este caso. Si lo tiene instalado, entonces se pueden escribir sus dos líneas:

tail -n 1000 myscript.log | sponge myscript.log

Normalmente, leer de un archivo al mismo tiempo que está escribiendo no es confiable. spongeresuelve esto al no escribir myscript.loghasta después de que tailhaya terminado de leerlo y terminado la tubería.

Instalar en pc

Para instalar spongeen un sistema similar a Debian:

apt-get install moreutils

Para instalar spongeen un sistema RHEL / CentOS, agregue el repositorio EPEL y luego haga:

yum install moreutils

Documentación

De man sponge:

spongelee la entrada estándar y la escribe en el archivo especificado. A diferencia de un redireccionamiento de shell, spongeabsorbe toda su entrada antes de escribir el archivo de salida. Esto permite construir tuberías que leen y escriben en el mismo archivo.

John1024
fuente
2
+1 Gracias, no lo sabía sponge. Muy útil para todos aquellos que aprendieron de la manera difícil que no puedes hacer sort importantfile.txt > importantfile.txt:)
dr01
4

definitivamente "tail + mv" es mucho mejor! Pero para gnu sed podemos intentar

sed -i -e :a -e '$q;N;101,$D;ba' log
JJoao
fuente
3

Para el registro, con edusted podría hacer algo como

ed -s infile <<\IN
0r !tail -n 1000 infile
+1,$d
,p
q
IN

Esto se abre infiley rpasa a la salida de tail -n 1000 infile(es decir, inserta esa salida antes de la primera línea) y luego se elimina de lo que inicialmente era la primera línea hasta el final del archivo. Reemplace ,pcon wpara editar el archivo en el lugar.
Sin embargo, tenga en cuenta que las edsoluciones no son adecuadas para archivos grandes.

don_crissti
fuente
0

Lo que puede hacer en su script es implementar la lógica de rotación de registros. Haga todo el registro a través de una función:

log()
{
   ...
}

Esta función, en primer lugar, hace algo como:

printf "%s\n" "$*" >> logfile

luego, verifica el tamaño del archivo o de alguna manera decide que el archivo requiere rotación. En ese punto, el archivo logfile.1, si existe, se elimina, el archivo logfile.0, si existe, se renombra logfile.1y logfilese le cambia el nombre logfile.0.

Decidir si rotar podría basarse en un contador mantenido en el script mismo. Cuando llega a 1000, se restablece a cero.

Si siempre se requiere recortar estrictamente a 1000 líneas, la secuencia de comandos podría contar el número de líneas en el archivo de registro cuando se inicie e inicializar el contador en consecuencia (o si el recuento ya cumple o excede 1000, realice la rotación de inmediato).

O puede obtener el tamaño, como con wc -c logfiley hacer la rotación en función de exceder un cierto tamaño. De esta manera, el archivo nunca tiene que ser escaneado para determinar la condición.

Kaz
fuente
0

Utilicé, en lugar de mv, el cpcomando para lograrlo, que puede tener algunos archivos de registro en el lugar donde se está ejecutando un Software. Tal vez en el directorio de inicio del Usuario diferente o en el directorio de la aplicación y tenga todos los registros en un solo lugar como enlaces duros. Si usa el mvcomando, pierde el enlace duro. Si usa el cpcomando en su lugar, mantendrá este enlace rígido.

mi código es algo como:

TMP_FILE="$(mktemp "${TMPFILENAME}.XXX")"

for FILE in "${LOGFILE_DIR}"/* ; do
    tail -n $MAXLINES "${FILE}" > "${TMP_FILE}"
    if [ $(ls -g "${TMP_FILE}" | awk '{print $4}') -lt $(ls -g "${FILE}" | awk '{print $4}') ] ; then
        cp "${TMP_FILE}" "${FILE}"
    fi
done   

Por lo tanto, si los archivos están en el mismo sistema de archivos, también puede otorgar algunos derechos diferentes a los usuarios y ${LOGFILE_DIR}modificar la longitud como lo hago yo.

Si es el mvcomando, pierde el enlace rígido entre los archivos y, por lo tanto, su segundo archivo no está más conectado al primero, tal vez se ubique en otro lugar.

Si en el otro lugar no permites que alguien borre el archivo, tus registros permanecerán juntos y serán controlados a través de tu propio script.

logrotateQuizás más agradable. Pero estoy contento con esta solución.

No se preocupe por el "" pero en mi caso hay algunos archivos con espacios y otras letras especiales y, si no hago el "" o el {}, todo no funciona bien.

Por ejemplo, hay un Dir donde los archivos más antiguos se comprimen automáticamente OLDFILE.zipy todo lo que se comprime también se enumera en Archivo, .zip_logpor lo que también .zip_logestá en este Dir pero en el LOGFILE_DIRque tengo con:

ln .zip_log "${LOGFILE_DIR}/USER_ZIP_log"

el archivo igual ya que es un enlace duro.

Andreas Bartels
fuente