
Tengo un archivo de registro que se actualiza continuamente (se agregó una nueva línea) después de un período de tiempo.
Solo obtengo mensajes de error del archivo cada 10 minutos.
Inicialmente, la primera vez busqué todas las líneas en un nuevo archivo con un patrón coincidente "ERROR ENCONTRADO" usandoawk.
Pero después de 10 minutos se agregó una línea nueva a un archivo de registro, por lo que quiero leer ese archivo de registro donde lo dejé. No quiero volver a empezar desde el principio.
¿Alguien puede sugerirme el mejor código o script para esto?
Respuesta1
Si abre el archivo en un descriptor de archivo como:
exec 3< /path/to/log/file
Luego podrás procesarlo:
awk '...' <&3
Después de lo cual fd 3 señalará donde awk
lo dejó.
10 minutos más tarde, desde la misma invocación de shell, puedes ejecutar eso
awk '...' <&3
comando nuevamente para procesar los nuevos datos.
Si desea guardar la posición en la que se encontraba, para poder reanudar la lectura desde una invocación de shell diferente, con ksh93
, puede hacer:
#! /usr/bin/env ksh93
file=/path/to/some-file
offset_file=$file.offset
exec 3< "$file"
[ -f "$offset_file" ] && exec 3<#(($(<"$offset_file")))
awk '...' <&3
echo "$(3<#((CUR)))" > "$offset_file"
O con zsh:
#! /usr/bin/env zsh
zmodload zsh/system
file=/path/to/some-file
offset_file=$file.offset
exec 3< $file
[ -f "$offset_file" ] && sysseek -u 3 "$(<$offset_file)"
awk '...' <&3
echo $((systell(3))) > $offset_file
Respuesta2
Me gusta la respuesta de Stéphane porque no lee el archivo completo una y otra vez, así que agrego aquí elintento(en Linux) equivalente a su solución (bash no tiene ninguna función incorporada seek
ni tell
capacidad). Habría usado un comentario pero mi reputación es demasiado baja.
LASTPOS=/tmp/saved_pos
exec 3< "$1"
test -f "$LASTPOS" && STARTPOS=$(($(<$LASTPOS)+1))
tail -c "+${STARTPOS:-1}" <&3 | grep "ERROR FOUND"
grep '^pos:' /proc/self/fdinfo/3 | cut -f2 > "$LASTPOS"
También reemplacé el awk
comando con grep
porque suele ser más rápido. Puede canalizar la salida a un awk
comando si necesita más procesamiento.
Respuesta3
Lo intentaría con wc -l
y tail
.
Si estás usando bash, esto debería funcionar:
#!/bin/bash
LASTLNFILE=/tmp/lastline # replace with a suitable path
test -f $LASTLNFILE && LASTLN=$(<$LASTLNFILE)
CURLN=$(wc -l $1 | cut -d' ' -f1)
if ((CURLN-LASTLN > 0)); then
tail -n $((CURLN-LASTLN)) $1
fi
echo $CURLN > $LASTLNFILE
PD: úselo como filtro antes de su programa awk, por ejemplo (suponiendo que lo haya llamado 'newlines.sh'):
./newlines.sh <log_file> | awk -f <your_awk_program>`
Dejo el script anterior como ejemplo de cómono lo hagas. Justo después de escribirlo, me di cuenta de que es vulnerable a una condición de carrera, siempre que el archivo de registro se actualiza mientras se ejecuta el script.
Es preferible un enfoque AWK puro:
#!/bin/awk
BEGIN {
lastlinefile = "/tmp/lastlinefile"
getline lastline < lastlinefile
}
NR > lastline && /ERROR FOUND/ {
# do your stuff...
print
}
END { print NR > lastlinefile }