19

サイズが約 35 GB の Apache access.log ファイルがあります。それをグリーピングすることは、多くのことを待つことなく、もはやオプションではありません。

日付を分割基準として使用して、多くの小さなファイルに分割したかったのです。

日付の形式は です[15/Oct/2011:12:02:02 +0000]。bash スクリプト、標準のテキスト操作プログラム (grep、awk、sed など)、パイプ、リダイレクトのみを使用して、どのようにすればよいでしょうか?

入力ファイル名はaccess.log. 出力ファイルを次のような形式にしたいと思いますaccess.apache.15_Oct_2011.log(並べ替え時にはうまくいきませんが、それでうまくいきます)。

4

7 に答える 7

22

を使用した片道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]] = sprintf("%02d", a)
}
{
    split($4,array,"[:/]")
    year = array[3]
    month = 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 MB のログ ファイルに対して、3.4 GHz 8 コア Xeon E31270 では、chepner による回答は 70 秒かかりましたが、この方法では 5 秒かかりました。

元のインスピレーション: 「既存の Apache ログファイルを月ごとに分割する方法は?

于 2012-07-30T01:14:29.180 に答える
10

純粋な bash で、アクセス ログを 1 回通過します。

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
于 2012-07-27T13:44:26.670 に答える
4

Perl が助けに来ました。

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

正確には「標準的な」操作プログラムではありませんが、それでもテキスト操作用に作成されています。

また、ファイル名の引数の順序を変更して、ファイルが access.apache.yyyy_mon_dd.log のように名前が付けられるようにして、並べ替えを容易にしました。

于 2012-07-27T12:38:46.857 に答える
4

これは、awk語彙的にソート可能なログ ファイルを出力するバージョンです。

いくつかの効率の向上: すべてが 1 つのパスで実行され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
于 2012-07-27T14:26:46.243 に答える
3

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
}'
于 2015-01-20T21:42:57.060 に答える
1

ちょっと醜い、それはあなたのための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
于 2012-07-27T12:45:28.893 に答える
0

非常に大きなログ ファイルを処理するときに進行状況を確認できるように、Theodore の回答をわずかに改善しました。

#!/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また、これを Mac OS X で動作させるには(brew install gawk持っていない場合)を使用する必要があることもわかりました。

于 2014-05-01T05:15:11.987 に答える