tailとgrepを使用して関心のある行をフィルタリングし、定期的にリアルタイムで追跡するログファイルがあります。しかし、これらの行には常に興味のないデータがたくさん含まれていますが、行内で必要な部分だけを見ることができるように解析することは困難です。各行エントリの形式は、主にリストラベルと引用符(時にはスペースを含む)で囲まれたデータです。以下は、(削除された)ログ行の例です。
2017:11:29-11:29:56 filter-1 httpproxy[3194]: id="0001" severity="info" sys="SecureWeb" sub="http" name="http access" action="pass" method="CONNECT" srcip="10.11.12.13" dstip="14.3.1.4" user="" group="" ad_domain="" statuscode="200" cached="0" profile="REF_HttPro1234 (Campus2)" filteraction="REF_HttStu (Allow Policy)" size="6518" request="0x915a3e00" url="https://website.net/" referer="" error="" authtime="0" dnstime="1" cattime="73" avscantime="0" fullreqtime="61576999" device="0" auth="6" ua="" exceptions="" category="9998" reputation="unverified" categoryname="Uncategorized" country="United States" application="krux" app-id="826"
2017:11:29-11:29:56 filter-1 httpproxy[3194]: id="0001" severity="info" sys="SecureWeb" sub="http" name="http access" action="pass" method="GET" srcip="10.13.14.15" dstip="154.6.75.10" user="" group="" ad_domain="" statuscode="200" cached="0" profile="REF_HttPro1235 (Campus1)" filteraction="REF_HttStu (Allow Policy)" size="3161" request="0x6b4d5610" url="http://host.com/mini_banner.png" referer="http://www.web.com/computers.htm" error="" authtime="0" dnstime="0" cattime="64" avscantime="848" fullreqtime="50046" device="0" auth="6" ua="Mozilla/5.0 (X11; CrOS x86_64 9765.85.0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/61.0.3163.123 Safari/537.36" exceptions="" category="111" reputation="trusted" categoryname="Education/Reference" sandbox="-" content-type="image/png"
1つの注意点は、すべてのラベルがすべての行に表示されるわけではないことです。たとえば、application と app-id は最初の行には表示されますが、2 番目の行には表示されません。
上記の行を入力の例として使用して出力として必要な例は、srcip、Categoryname、およびurlタグのみを順番に表示することです。希望の出力は次のとおりです。
10.11.12.13 Uncategorized https://website.net/
10.13.14.15 Education/Reference http://host.com/mini_banner.png
私は表示されたラベルをすぐに調整できるように調整しやすい解決策を探しています。
答え1
お客様のデータは高度に構造化されています。キー="値"したがって、gnu awkを使用してキー名のリストを引数として使用し、その値のみを印刷する小さなシェルスクリプトを作成できます。例えばmyscript
:
#!/bin/bash
awk -v lhs="$*" '
BEGIN{ FPAT = "[a-z-]*=\"[^\"]*\""
nwant = split(lhs,want)
}
{ for(i=1;i<=NF;i++){
start = match($i,/([a-z-]*)="([^"]*)"/,a)
key[a[1]] = a[2]
}
for(i=1;i<=nwant;i++){printf "%s ",key[want[i]]; key[want[i]] = ""}
printf "\n"
}'
これmyscript srcip categoryname url
により、awk変数は最初に配列に分割されるlhs
単一の文字列に設定されます。want
行は awk によってパターンに一致するフィールドに分割されます。キー="値"組み込みFPAT
変数を使用します。
各行の各フィールドに対して2つのキャプチャグループに分割しますmatch()
。 1つはキー用で、もう1つは二重引用符内の部分用です。これはawkによって配列に格納され、キー文字列でインデックス付けされた連想配列a
に保存されます。key
次に、必要な各キーの値を印刷し、その行にそのキーがない場合は、次の行の値を消去します。明らかに、これはすべてのデータが必要な構造を持っていると仮定し、アルファベット以外の文字を持つ値またはキー内で( ")を処理するように変更する必要があります。
4.0より前のGNU awk(gawk)バージョンには、パターンに一致するフィールドにFPAT
行を分割する機能が組み込まれていないため、これを直接実行する必要がありました。
#!/bin/bash
awk -v lhs="$*" '
BEGIN{ nwant = split(lhs,want) }
{ input = $0
while(match(input,"[a-z-]*=\"[^\"]*\"")>0){
field = substr(input,RSTART,RLENGTH)
input = substr(input,RSTART+RLENGTH)
start = match(field,/([a-z-]*)="([^"]*)"/,a)
key[a[1]] = a[2]
}
for(i=1;i<=nwant;i++){printf "%s ",key[want[i]]; key[want[i]] = ""}
printf "\n"
}'
明らかに、2つの一致呼び出しを1つにまとめることができますが、これは元の一致との違いを示しています。
答え2
使用(POSIX規格)sed
...
sed 's/.* srcip="\([^"]*\)" .* url="\([^"]*\)" .* categoryname="\([^"]*\)" .*/\1 \3 \2/' logfile
ここには素晴らしい内容はありません。キーを見つけて値を括弧で囲み、逆\(..\)
参照として使用できます。次に、文字列を必要に応じてソートされたスペースで区切られた逆参照に置き換えます\1 \3 \2
。
出力:
10.11.12.13 Uncategorized https://website.net/
10.13.14.15 Education/Reference http://host.com/mini_banner.png
ログにこれらのキーがすべて含まれていない文字列が含まれている場合は、次のものを使用できます。
sed -n 's/.* srcip="\([^"]*\)" .* url="\([^"]*\)" .* categoryname="\([^"]*\)" .*/\1 \3 \2/p' logfile
これにより、パターンに一致する行だけが印刷されます。
もちろん、ストリーミングに使用するには、ファイル名を削除して次のようにします。[something sending logs to stdout] | sed ...