Как выполнить вычисление по файлу журнала
Вопрос
У меня есть приложение, которое выглядит примерно так:
I, [2009-03-04T15:03:25.502546 #17925] INFO -- : [8541, 931, 0, 0]
I, [2009-03-04T15:03:26.094855 #17925] INFO -- : [8545, 6678, 0, 0]
I, [2009-03-04T15:03:26.353079 #17925] INFO -- : [5448, 1598, 185, 0]
I, [2009-03-04T15:03:26.360148 #17925] INFO -- : [8555, 1747, 0, 0]
I, [2009-03-04T15:03:26.367523 #17925] INFO -- : [7630, 278, 0, 0]
I, [2009-03-04T15:03:26.375845 #17925] INFO -- : [7640, 286, 0, 0]
I, [2009-03-04T15:03:26.562425 #17925] INFO -- : [5721, 896, 0, 0]
I, [2009-03-04T15:03:30.951336 #17925] INFO -- : [8551, 4752, 1587, 1]
I, [2009-03-04T15:03:30.960007 #17925] INFO -- : [5709, 5295, 0, 0]
I, [2009-03-04T15:03:30.966612 #17925] INFO -- : [7252, 4928, 0, 0]
I, [2009-03-04T15:03:30.974251 #17925] INFO -- : [8561, 4883, 1, 0]
I, [2009-03-04T15:03:31.230426 #17925] INFO -- : [8563, 3866, 250, 0]
I, [2009-03-04T15:03:31.236830 #17925] INFO -- : [8567, 4122, 0, 0]
I, [2009-03-04T15:03:32.056901 #17925] INFO -- : [5696, 5902, 526, 1]
I, [2009-03-04T15:03:32.086004 #17925] INFO -- : [5805, 793, 0, 0]
I, [2009-03-04T15:03:32.110039 #17925] INFO -- : [5786, 818, 0, 0]
I, [2009-03-04T15:03:32.131433 #17925] INFO -- : [5777, 840, 0, 0]
Я бы хотел создать сценарий оболочки, который вычисляет среднее значение 2-го и 3-го полей в скобках (840
и 0
в последнем примере).Еще более сложный вопрос:возможно ли получить среднее значение по 3-му полю только тогда, когда последнего нет 0
?
Я знаю, что мог бы использовать Ruby
или другой язык для создания скрипта, но я бы хотел сделать это на Bash
.Любые хорошие предложения по ресурсам или подсказки о том, как создать такой скрипт, помогли бы.
Решение
Здесь также публикуется ответ, который я вставил вам через IM, просто потому, что он заставляет меня попробовать StackOverflow:)
# replace $2 with the column you want to avg;
awk '{ print $2 }' | perl -ne 'END{ printf "%.2f\n", $total/$n }; chomp; $total+= Здесь также публикуется ответ, который я вставил вам через IM, просто потому, что он заставляет меня попробовать StackOverflow:)
<*>; $n++' < log
Другие советы
Использование bash
и awk
:
cat file | sed -ne 's:^.*INFO.*\[\([0-9, ]*\)\][ \r]*$:\1:p' | awk -F ' *, *' '{ sum2 += $2 ; sum3 += $3 } END { if (NR>0) printf "avg2=%.2f, avg3=%.2f\n", sum2/NR, sum3/NR }'
Пример вывода (для ваших исходных данных):
avg2=2859.59, avg3=149.94
Конечно, вам не нужно использовать cat
, он включен туда для удобства чтения и для иллюстрации того факта , что входные данные могут поступать из любого канала;если вам нужно поработать с существующим файлом, запустите sed -ne '...' file | ...
напрямую.
Редактировать
Если у вас есть доступ к gawk
(GNU awk), вы можете устранить необходимость в sed
следующим образом:
cat file | gawk '{ if(match($0, /.*INFO.*\[([0-9, ]*)\][ \r]*$/, a)) { cnt++; split(a[1], b, / *, */); sum2+=b[2]; sum3+=b[3] } } END { if (cnt>0) printf "avg2=%.2f, avg3=%.2f\n", sum2/cnt, sum3/cnt }'
Те же замечания повторяются. cat
применять.
Небольшое объяснение:
sed
печатает только строки (-n ... :p
комбинация), которые соответствуют регулярному выражению (строки, содержащие ИНФОРМАЦИЮ, за которыми следует любая комбинация цифр, пробелов и запятых в квадратных скобках в конце строки, с учетом завершающих пробелов и CR);если какая-либо такая строка совпадает, сохраните только то, что заключено в квадратные скобки (\1
, соответствующий тому, что находится между\(...\)
в регулярном выражении) перед печатью (:p
)- sed выведет строки, которые выглядят следующим образом:
8541, 931, 0, 0
- sed выведет строки, которые выглядят следующим образом:
awk
использует запятую , окруженную 0 или более пробелами (-F ' *, *'
) в качестве разделителей полей;$1
соответствует первому столбцу (например ,8541),$2
ко второму и т.д.Отсутствующие столбцы считаются значениями0
- в конце,
awk
разделяет аккумуляторыsum2
и т.д. по количеству обработанных записей,NR
- в конце,
gawk
делает все одним выстрелом;сначала он проверит, соответствует ли каждая строка одному и тому же регулярному выражению, переданному в предыдущем примереsed
(за исключением того, что в отличие отsed
,awk
не требует наличия\
в круглых скобках, ограничивающих области или интересы).Если строка совпадает, то то, что находится между круглыми скобками, заканчивается в [1], которое затем мы разделяем, используя тот же разделитель (запятую, окруженную любым количеством пробелов), и используем это для накопления.Я представилcnt
вместо того, чтобы продолжать использоватьNR
потому что количество обработанных записейNR
может быть больше фактического количества соответствующих записей (cnt
) если не все строки имеют видINFO ... [...comma-separated-numbers...]
, чего не было в случае сsed|awk
с тех пор какsed
гарантируется, что все строки будут переданы вawk
были актуальны.
Используйте nawk или / usr / xpg4 / bin / awk в Solaris .
awk -F'[],]' 'END {
print s/NR, t/ct
}
{
s += $(NF-3)
if ($(NF-1)) {
t += $(NF-2)
ct++
}
}' infile
Использовать Python
logfile= open( "somelogfile.log", "r" )
sum2, count2= 0, 0
sum3, count3= 0, 0
for line in logfile:
# find right-most brackets
_, bracket, fieldtext = line.rpartition('[')
datatext, bracket, _ = fieldtext.partition(']')
# split fields and convert to integers
data = map( int, datatext.split(',') )
# compute sums and counts
sum2 += data[1]
count2 += 1
if data[3] != 0:
sum3 += data[2]
count3 += 1
logfile.close()
print sum2, count2, float(sum2)/count2
print sum3, count3, float(sum3)/count3