
У нас есть файл журнала, который я часто просматриваю в режиме реального времени с помощью 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, который принимает в качестве аргумента список имен ключей и просто выводит эти значения. Например, 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 в массив a
, и мы сохраняем их в ассоциативном массиве, key
индексируемом строкой ключа.
Затем для каждого нужного ключа мы печатаем значение и очищаем его для следующей строки (в случае, если в этой строке нет этого ключа). Очевидно, это предполагает, что все данные имеют требуемую структуру и потребуются изменения для обработки (") внутри значения или ключей с неалфавитными символами.
Версии gnu awk (gawk) до 4.0 не имеют 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 ...