Разделить файл access.log по датам с помощью инструментов командной строки - программирование
Подтвердить что ты не робот

Разделить файл access.log по датам с помощью инструментов командной строки

У меня есть файл apache access.log размером около 35 ГБ. Грепинг через него уже не вариант, не дожидаясь многого.

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

Дата находится в формате "[15/окт/2011:12: 02: 02 +0000]". Любая идея, как мне это сделать, используя только bash скрипты, стандартные программы обработки текста (grep, awk, sed и т.д.), Трубопровод и перенаправление?

Имя входного файла - access.log. Я хотел бы, чтобы выходные файлы имели формат, такой как access.apache.15_Oct_2011.log(это могло бы сделать трюк, хотя это не приятно при сортировке..)

4b9b3361

Ответ 1

Один из способов: awk:

awk 'BEGIN {
    split("Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec ", months, " ")
    for (a = 1; a <= 12; a++)
        m[months[a]] = a
}
{
    split($4,array,"[:/]");
    year = array[3]
    month = sprintf("%02d", m[array[2]])

    print > FILENAME"-"year"_"month".txt"
}' incendiary.ws-2009

Это приведет к выходу таких файлов, как:

incendiary.ws-2010-2010_04.txt
incendiary.ws-2010-2010_05.txt
incendiary.ws-2010-2010_06.txt
incendiary.ws-2010-2010_07.txt

Против файла журнала размером 150 МБ, ответ chepner занял 70 секунд на 3,8 ГГц 8 Core Xeon E31270, тогда как этот метод занял 5 секунд.

Оригинальное вдохновение: Как разбить существующий файл журнала apache по месяцам?

Ответ 2

Pure bash, заставляя пройти через журнал доступа:

while read; do
    [[ $REPLY =~ \[(..)/(...)/(....): ]]

    d=${BASH_REMATCH[1]}
    m=${BASH_REMATCH[2]}
    y=${BASH_REMATCH[3]}

    #printf -v fname "access.apache.%s_%s_%s.log" ${BASH_REMATCH[@]:1:3}
    printf -v fname "access.apache.%s_%s_%s.log" $y $m $d

    echo "$REPLY" >> $fname
done < access.log

Ответ 3

Perl пришел на помощь:

cat access.log | perl -n -e'[email protected]\[(\d{1,2})/(\w{3})/(\d{4}):@; open(LOG, ">>access.apache.$3_$2_$1.log"); print LOG $_;'

Ну, это не совсем "стандартная" программа манипуляции, но тем не менее она сделана для манипуляции с текстом.

Я также изменил порядок аргументов в имени файла, чтобы файлы назывались access.apache.yyyy_mon_dd.log для упрощения сортировки.

Ответ 4

Вот версия awk, которая выводит лексически сортируемые файлы журналов.

Некоторые улучшения эффективности: все выполняется за один проход, генерирует только fname, когда он не совпадает с предыдущим, закрывает fname при переключении на новый файл (в противном случае у вас могут закончиться файловые дескрипторы).

awk -F"[]/:[]" '
BEGIN {
  m2n["Jan"] =  1;  m2n["Feb"] =  2; m2n["Mar"] =  3; m2n["Apr"] =  4;
  m2n["May"] =  5;  m2n["Jun"] =  6; m2n["Jul"] =  7; m2n["Aug"] =  8;
  m2n["Sep"] =  9;  m2n["Oct"] = 10; m2n["Nov"] = 11; m2n["Dec"] = 12;
}
{
  if($4 != pyear || $3 != pmonth || $2 != pday) {
    pyear  = $4
    pmonth = $3
    pday   = $2

    if(fname != "")
      close(fname)

    fname  = sprintf("access_%04d_%02d_%02d.log", $4, m2n[$3], $2)
  }
  print > fname
}' access-log

Ответ 5

Вид уродливого, что bash для вас:

    for year in 2010 2011 2012; do
       for month in jan feb mar apr may jun jul aug sep oct nov dec; do
           for day in 1 2 3 4 5 6 7 8 9 10 ... 31 ; do
               cat access.log | grep -i $day/$month/$year > $day-$month-$year.log
            done
        done
     done

Ответ 6

Я объединил решения Theodore и Thor для повышения эффективности эффективности и ежедневных файлов Thor, но сохранил исходную поддержку адресов IPv6 в файле с объединенным форматом.

awk '
BEGIN {
  m2n["Jan"] =  1;  m2n["Feb"] =  2; m2n["Mar"] =  3; m2n["Apr"] =  4;
  m2n["May"] =  5;  m2n["Jun"] =  6; m2n["Jul"] =  7; m2n["Aug"] =  8;
  m2n["Sep"] =  9;  m2n["Oct"] = 10; m2n["Nov"] = 11; m2n["Dec"] = 12;
}
{
  split($4, a, "[]/:[]")
  if(a[4] != pyear || a[3] != pmonth || a[2] != pday) {
    pyear  = a[4]
    pmonth = a[3]
    pday   = a[2]

    if(fname != "")
      close(fname)

    fname  = sprintf("access_%04d-%02d-%02d.log", a[4], m2n[a[3]], a[2])
  }
  print >> fname
}'

Ответ 7

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

#!/usr/bin/awk -f

BEGIN {
    split("Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec ", months, " ")
    for (a = 1; a <= 12; a++)
        m[months[a]] = a
}
{
    split($4, array, "[:/]")
    year = array[3]
    month = sprintf("%02d", m[array[2]])

    current = year "-" month
    if (last != current)
        print current
    last = current

    print >> FILENAME "-" year "-" month ".txt"
}

Также я обнаружил, что мне нужно использовать gawk (brew install gawk, если у вас его нет), чтобы это работало в Mac OS X.