возобновить чтение файла журнала с того места, где я его оставил в прошлый раз

возобновить чтение файла журнала с того места, где я его оставил в прошлый раз

У меня есть файл журнала, который постоянно обновляется (добавляется новая строка) через несколько периодов времени.

Я извлекаю из файла только сообщения об ошибках каждые 10 минут.

Первоначально, в первый раз я извлек все строки в новый файл с соответствующим шаблоном «НАЙДЕНА ОШИБКА», используяawk.

Но через 10 минут в файл журнала была добавлена ​​новая строка, поэтому я хочу прочитать этот файл журнала, на котором я остановился. Я не хочу начинать с самого начала.

Может ли кто-нибудь предложить мне лучший код или скрипт для этого?

решение1

Если вы откроете файл с помощью дескриптора файла, например:

exec 3< /path/to/log/file

Затем вы можете обработать его:

awk '...' <&3

После чего fd 3 укажет туда, где awkон был оставлен.

10 минут спустя, из того же вызова оболочки, вы можете запустить это

awk '...' <&3

повторите команду для обработки новых данных.

Если вы хотите сохранить позицию, в которой вы находились, чтобы можно было продолжить чтение из другого вызова оболочки, с помощью ksh93, вы можете сделать следующее:

#! /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"

Или с 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

решение2

Мне нравится ответ Стефана, потому что он не перечитывает весь файл снова и снова, поэтому я добавляю сюдаБаш(на Linux) эквивалент его решения (bash не имеет встроенных seekили tellвозможностей). Я бы использовал комментарий, но моя репутация слишком низкая.

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"

Я также заменил awkкоманду на a, grepпотому что это обычно быстрее. Вы можете передать вывод в awkкоманду, если вам нужна дополнительная обработка.

решение3

Я бы попробовал с wc -lи tail.
Если вы используете bash, это должно сработать:

#!/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

P.S. используйте его как фильтр перед вашей программой awk, например (предполагая, что вы назвали ее «newlines.sh»):

./newlines.sh <log_file> | awk -f <your_awk_program>`

Я оставляю приведенный выше скрипт в качестве примера того, какне делай этого. Сразу после написания я понял, что он уязвим к состоянию гонки, когда файл журнала обновляется во время работы скрипта.

Предпочтительнее использовать чистый AWK-подход:

#!/bin/awk

BEGIN { 
  lastlinefile = "/tmp/lastlinefile"
  getline lastline < lastlinefile
}

NR > lastline && /ERROR FOUND/ {
  # do your stuff...
  print
}

END { print NR > lastlinefile }

Связанный контент