Как выполнить расчет по лог-файлу - PullRequest
1 голос
/ 05 марта 2009

У меня есть что выглядит так:

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. Любые хорошие предложения относительно ресурсов или советы о том, как создать такой скрипт, могут помочь.

Ответы [ 4 ]

6 голосов
/ 05 марта 2009

Используйте 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 | ... напрямую.


EDIT

Если у вас есть доступ к 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), которые соответствуют регулярному выражению (строки, содержащие INFO, за которыми следует любая комбинация цифр, пробелов и запятых между квадратными скобками в конце строки, с учетом конечных пробелов и CR); если такая строка совпадает, перед печатью сохраняйте только то, что находится в квадратных скобках (\1, что соответствует значению между \(...\) в регулярном выражении) (:p)
    • sed выведет строки, которые выглядят так: 8541, 931, 0, 0
  • 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, будут релевантными.
1 голос
/ 05 марта 2009

Здесь также публикуется ответ, который я вставил вам через IM, просто потому, что он заставляет меня попробовать StackOverflow:)

# replace $2 with the column you want to avg; 
awk '{ print $2 }' | perl -ne 'END{ printf "%.2f\n", $total/$n }; chomp; $total+= $_; $n++' < log
0 голосов
/ 05 марта 2009

Использовать 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
0 голосов
/ 05 марта 2009

Использование 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
...