ログファイルから特定の情報を取得する

ログファイルから特定の情報を取得する

ログファイルからいくつかの部分を取得したいのですが、ユーザー、モジュール、アクション、doAjax、ajaxActionを取得するためにリクエスト部分をカットしようとしました。

例えば、私は

195.xx.x.x - - [25/Apr/2017:09:60:xx +0200] "POST /userx/index.php?m=contacts&a=form&...
192.xx.x.x - - [25/Apr/2017:09:45:xx +0200] "POST /usery/index.php?m=customer&doajax=request&action=getContacts...
197.xx.x.x - - [25/Apr/2017:09:20:xx +0200] "GET /userx/index.php?m=meeting&doajax=date&id=3

そして私は以下を持ちたいです:

[user]|[module]|[action]|[doAjax]|[ajaxAction] 
usery  contacts  form     null     null
userx  customer  null     request  getContacts
userz  meeting   null     date     null

どこ:

userx --> user  
m=xxx -->module   
a=xxx -->action  
doajax=xxx-->doAjax   
action=xxx-->ajaxAction  

、setを使用しようとしましたawkが、次のコマンドでリクエストを見つけることができる 7 番目の列だけを切り取りました。

awk '{printf $7; next ; }' logfile

では、リクエストだけを印刷した後、ユーザー、モジュール、アクション、doAjax、ajaxAction を抽出するにはどうすればよいのでしょうか?

答え1

Perl の「ワンライナー」:

$ perl -lne '
BEGIN{
    printf "%-10s%-10s%-10s%-10s%-15s\n", qw([user] [module] [action] [doAjax] [ajaxAction]);
} 
$usr = $mde = $act = $doAj = $ajAc = "null"; 
$usr=$1 if m|\s/([^/]+)/|; 
$mde=$1 if /m=(.+?)(&|$)/; 
$act=$1 if /a=(.+?)(&|$)/; 
$doAj=$1 if /doajax=(.+?)(&|$)/; 
$ajAc=$1 if /action=(.+?)(&|$)/; 
printf "%-10s%-10s%-10s%-10s%-15s\n", ($usr,$mde,$act,$doAj,$ajAc)' file 
[user]    [module]  [action]  [doAjax]  [ajaxAction]   
userx     contacts  form      null      null           
usery     customer  null      request   getContacts    
userx     meeting   null      date      null           

ここでの基本的なトリックは、URL 部分を識別する各文字列を検索し、見つかった場合は対応する変数を設定することです。いずれの場合も、識別子の後に が続き=(例m=)、次に または&行末 が続く を探します(&|$)。一致した部分は括弧で囲まれるため (例m=(.+?))、それを として参照することができ$2、それが各変数に保存されます。

本当に区切り文字が必要で|、出力が読みにくくなることに異論がない場合は、代わりにこれを使用できます。

$ perl -lne '
BEGIN{
    printf "%s|%s|%s|%s|%s\n", qw([user] [module] [action] [doAjax] [ajaxAction]);
} 
$usr = $mde = $act = $doAj = $ajAc = "null"; 
$usr=$1 if m|\s/([^/]+)/|; 
$mde=$1 if /m=(.+?)(&|$)/; 
$act=$1 if /a=(.+?)(&|$)/; 
$doAj=$1 if /doajax=(.+?)(&|$)/; 
$ajAc=$1 if /action=(.+?)(&|$)/; 
print join "|", ($usr,$mde,$act,$doAj,$ajAc)' file 
[user]|[module]|[action]|[doAjax]|[ajaxAction]
userx|contacts|form|null|null
usery|customer|null|request|getContacts
userx|meeting|null|date|null

より良い(より読みやすい出力)アプローチは、printf代わりに以下を使用することです。

答え2

これを awk で実行したい場合は、次のようにします。 Split を使用すると、任意のフィールド区切り文字を使用して文字列を分割できます。

awk  '{split($7,a,"/"); split(a[3],b,"m="); split(b[2],c,"&"); split(c[2],d,"="); print a[2], c[1], d[1], d[2] }' logfile

これにより、必要な列が生成されます。

userx contacts a form
usery customer doajax request
userx meeting doajax date

残りのステップはフォーマットです。awkの配列は連想配列であり、文字列でインデックス付けできます。ここ次のようにすることができます。ここで、op (output の略) は null に初期化されます。次に、 を設定しますop[d[1]]=d[2]

awk  '{split($7,a,"/"); split(a[3],b,"m="); split(b[2],c,"&"); split(c[2],d,"="); op["a"]="null"; op["doajax"]="null"; op["ajaxaction"]="null"; op[d[1]]=d[2];print a[2], c[1], op["a"], op["doajax"], op["ajaxaction"] }' junk.txt 

[変更後]

awk  '{split($7,a,"/"); split(a[3],b,"m="); split(b[2],c,"&"); split(c[2],d,"="); op["a"]="null"; op["doajax"]="null"; op["action"]="null"; op[d[1]]=d[2]; split(c[3],f,"="); split(f[2],g,"."); op[f[1]]=g[1]; print a[2], c[1], op["a"], op["doajax"], op["action"] }' junk.txt 

出力は次のようになります

userx contacts form null null
usery customer null request getContacts
userx meeting null date null

答え3

perl -lane '
BEGIN {
   print $H = join "|", map { s/.*/[$&]/r } @H = qw/user module action doAjax ajaxAction/;
   pos($H) = 0;
   push(@pos, pos($H)-$p), $p=pos($H) while $H =~ /\[/g;
   $fmt = join "", map { "\%-${_}s" } @pos[1..$#pos], length($H)-$p;
}

   my(%h, %H) = $F[-1] =~ /[?&]\K([^=]+)=([^&]+)/g;
   @H{@H} = ($F[-1] =~ m|^/([^/]+)|, @h{qw/m a doajax action/});
   print sprintf $fmt, map { $H{$_} // "null" } @H;
' logfile

結果

[user]|[module]|[action]|[doAjax]|[ajaxAction]
userx  contacts form     null     null
usery  customer null     request  getContacts
userx  meeting  null     date     null

説明

  1. Perl オプション:

    -l 作るORS = RS = \n

    -a@F現在のレコードを分割して得られた配列にフィールドを格納するので/\s+/、例えば、$F[0] => $1, $F[1] => $2, ..., $F[-1] => $NF

    -n 入力ファイルを行ごとに読み取り、要求されない限り出力を行わない暗黙のループを設定します。

  2. BEGINブロック:

    まず、ヘッダーを出力します。次に、ヘッダーに基づいて動的にフォーマットを決定します。読み取られる行ごとに、キーが = の前の文字列で値が = の後の文字列であるハッシュ %h を設定します。調べる文字列は、左側に ? または &、右側に & で区切られます。次に、キーが %h ハッシュの名前変更バージョンである別のハッシュ %H を設定します。次に、BEGIN ブロックで計算したフォーマットに基づいてハッシュを出力します。

関連情報