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

Обзор

В этом скрипте всего несколько мелких ошибок, и я бы изменил несколько стилистических вещей. Давайте построчно пройдемся по оригиналу:

#!/bin/bash

port=$1

Типичный #! и простое присваивание, у нас хорошее начало.

found=$(cat /etc/services | grep -o '[[:space:]]$port/' | wc -l)

В этой строке есть небольшая проблема, которую можно не заметить на первый взгляд. Поскольку $port находится в сильных кавычках, '…$port', он не будет расширяться до своего значения. Вы можете использовать слабые кавычки вместо "…$port". Кроме того, здесь вам не нужен кошка. grep принимает имя файла в качестве аргумента, но если это не так, вы всегда можете использовать перенаправление (grep 'шаблон' < /etc/services). Кроме того, grep -c записывает количество совпадающих строк, поэтому wc -l здесь также избыточен.

while [ $found -ge 1 ]; do
    $port=$($port+1)
done

Цикл кажется почти в порядке. Вы захотите указать сущности в тесте на случай, если $found может оказаться пустым. Кроме того, вы назначаете с помощью port=, а не $port= (но вы использовали правильную форму выше, поэтому я предполагаю, что это была просто опечатка). Наконец, $($port+1) означает (например, если порт равен 22) «Вывод команды 22+1".Очевидно, вам нужен «Результат арифметического выражения 22+1», который очень похож на $(($port+1)).

echo "found: $found"
echo "port: $port"

С этими строками все в порядке.

Учитывая логику

Теперь, если вы сделаете все вышеперечисленные изменения, чтобы программа стала синтаксически правильной, она все равно не будет делать то, что вы хотите. Вы не указали «правильный» вывод, поэтому я предполагаю, что вы хотите:

found: <the number of occurrences of $1 in /etc/services>
port: <the next free port>

Если эти предположения неверны, дайте мне знать в комментариях, и я отредактирую соответствующим образом.

Сценарий как есть никогда не вернется, если данный порт действительно появляется в /etc/services, потому что вы никогда не обновляете found. Таким образом, вы можете подумать, что было бы разумно скопировать строку found=… в цикл, но это не так! Если вы это сделаете, то скрипт всегда будет возвращать found: 0. Я думаю, что лучше всего здесь создать функцию:

found () {
    grep -co "[[:space:]]$1/" /etc/services
}

, но теперь выходное значение этой функции может напрямую работать как условие. Вы захотите заглушить вывод, чтобы он не отображался вместе с вашим, и тогда вам больше не понадобятся флаги -co, но вам понадобятся -q:

while found "$port"; do

Затем, чтобы вернуть количество вхождений порта ввода, это будет

echo "found: $(found "$1")"

или вы можете использовать printf.

Собираем все воедино

Я думаю, что это сценарий, который вы пытались написать:

#!/bin/sh

found () {
    grep -q "[[:space:]]$1/" /etc/services
}

port=$1

while found "$port"; do
    port=$(($port+1))
done

printf 'found: %d\nport %d\n' "$(found "$1")" "$port"

Бонус

Когда я писал это, моей первой реакцией было то, что подсчет строк, соответствующих заданному шаблону, может быть выполнен в awk, а также grep.Затем мне пришло в голову, что всю программу можно написать на awk. В качестве бонуса вот одна из его реализаций (хотя здесь я предполагаю, что /etc/services сортируется по номеру порта):

#!/usr/bin/awk -f

BEGIN {
    FS = "[ \t/][ \t/]*"
    ARGC = 2
    PORT = ARGV[1]
    OPORT = PORT
    ARGV[1] = "/etc/services"
}
($2 == OPORT) {
    FOUND = FOUND + 1
}
(NEXT == 0 && NR > 1 && $2 > PORT) {
    NEXT = ($2 > PORT + 1) ? PORT + 1 : NEXT
    PORT = $2
}
END {
    printf "found: %d\nport: %d\n", FOUND, (FOUND == 0) ? OPORT : NEXT
}
0
29.08.2018, 18:01
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

П.С. используйте его в качестве фильтра перед вашей программой 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 }
0
28.01.2020, 02:23

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

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"

Или с зш:

#! /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
28.01.2020, 02:23

Мне нравится ответ Стефана, потому что он не читает весь файл снова и снова, поэтому я добавляю здесь bash(в 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на grep, потому что она обычно быстрее. Вы можете направить вывод в команду awk, если вам нужна дальнейшая обработка.

1
28.01.2020, 02:23

Теги

Похожие вопросы