Разделить файл access.log по датам с помощью инструментов командной строки
У меня есть файл apache access.log размером около 35 ГБ. Грепинг через него уже не вариант, не дожидаясь многого.
Я хотел разбить его во многих небольших файлах, используя критерии даты для разделения.
Дата находится в формате "[15/окт/2011:12: 02: 02 +0000]". Любая идея, как мне это сделать, используя только bash скрипты, стандартные программы обработки текста (grep, awk, sed и т.д.), Трубопровод и перенаправление?
Имя входного файла - access.log. Я хотел бы, чтобы выходные файлы имели формат, такой как access.apache.15_Oct_2011.log(это могло бы сделать трюк, хотя это не приятно при сортировке..)
Ответы
Ответ 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.