
Eu tenho um arquivo de log que é atualizado continuamente (nova linha adicionada) após algum período de tempo.
Estou buscando apenas mensagens de erro do arquivo a cada 10 minutos.
Inicialmente, na primeira vez, busquei todas as linhas em um novo arquivo com um padrão correspondente "ERROR FOUND" usandoestranho.
Mas depois de 10 minutos, mais uma nova linha foi adicionada a um arquivo de log, então quero ler esse arquivo de log de onde deixei. Não quero começar do início novamente.
Alguém pode me sugerir o melhor código ou script para isso?
Responder1
Se você abrir o arquivo em um descritor de arquivo como:
exec 3< /path/to/log/file
Você pode então processá-lo:
awk '...' <&3
Depois disso, fd 3 apontará para onde awk
o deixou.
10 minutos depois, a partir da mesma invocação do shell, você pode executar isso
awk '...' <&3
comando novamente para processar os novos dados.
Se você quiser salvar a posição em que estava, para poder retomar a leitura de uma invocação de shell diferente, com ksh93
, você pode fazer:
#! /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"
Ou com 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
Responder2
Gosto da resposta do Stéphane porque ele não lê o arquivo inteiro repetidas vezes, então adiciono aqui ofesta(no Linux) equivalente à sua solução (o bash não tem recursos internos seek
ou tell
habilidades). Eu teria usado um comentário, mas minha reputação é muito baixa.
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"
Também substituí o awk
comando por a grep
porque geralmente é mais rápido. Você pode canalizar a saída para um awk
comando se precisar de processamento adicional.
Responder3
Eu tentaria com wc -l
e tail
.
Se você estiver usando o bash, isso deve 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
PS use-o como um filtro antes do seu programa awk, por exemplo (assumindo que você o nomeou 'newlines.sh'):
./newlines.sh <log_file> | awk -f <your_awk_program>`
Estou deixando o script acima como um exemplo de comonão faça isso. Logo depois de escrevê-lo, percebi que ele é vulnerável a uma condição de corrida, sempre que o arquivo de log é atualizado enquanto o script está em execução.
Uma abordagem AWK pura é preferível:
#!/bin/awk
BEGIN {
lastlinefile = "/tmp/lastlinefile"
getline lastline < lastlinefile
}
NR > lastline && /ERROR FOUND/ {
# do your stuff...
print
}
END { print NR > lastlinefile }