
我們有一個日誌文件,我經常用 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"
需要注意的一件事是,並非所有標籤都出現在每一行上。例如,application 和 app-id 出現在第一行中,但不在第二行。
使用上面的行作為範例輸入,我希望作為輸出的範例是僅按順序顯示 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 編寫一個小型 shell 腳本,該腳本將鍵名稱列表作為參數並僅列印這些值。例如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
變數。
在每一行上,對於每個字段,我們將其分為match()
2 個捕獲組,分別用於鍵和雙引號中的部分。這些由 awk 放入 array 中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
使用(符合 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 ...