使用Bash解析文件
我有一个包含多行数据的文件,如下所示:使用Bash解析文件,bash,parsing,Bash,Parsing,我有一个包含多行数据的文件,如下所示: {date=2017-01-01 time=23:59:59 logid=0000000001 srcip=123.123.123.123 srcport=2222 srcintf="Branches_Out" dstip=222.222.222.222 dstport=80 service="tcp/8080" appid=41469 app="Microsoft.Portal" apprisk=elevated applist="default" da
{date=2017-01-01 time=23:59:59 logid=0000000001 srcip=123.123.123.123 srcport=2222 srcintf="Branches_Out" dstip=222.222.222.222 dstport=80 service="tcp/8080" appid=41469 app="Microsoft.Portal" apprisk=elevated applist="default"
date=2017-01-01 time=24:00:00 logid=0000000002 srcip=124.124.124.124 srcport=3333 srcintf="Branches_Out" dstip=111.111.111.111 dstport=90 service="tcp/9090" appid=15893 app="HTTP.BROWSER" apprisk=elevated applist="default"}
{123.123.123.123, 222.222.222.222, 80, tcp/8080, "Microsoft.Portal"
124.124.124.124, 111.111.111.111, 90, tcp/9090, "HTTP.BROWSER"}
对于每一行,我需要一个Bash代码来查找(srcip=,dstip=,dstport=,service=,app=)
之后的特定数据值,并将其解析为新文件,新文件应如下所示:
{date=2017-01-01 time=23:59:59 logid=0000000001 srcip=123.123.123.123 srcport=2222 srcintf="Branches_Out" dstip=222.222.222.222 dstport=80 service="tcp/8080" appid=41469 app="Microsoft.Portal" apprisk=elevated applist="default"
date=2017-01-01 time=24:00:00 logid=0000000002 srcip=124.124.124.124 srcport=3333 srcintf="Branches_Out" dstip=111.111.111.111 dstport=90 service="tcp/9090" appid=15893 app="HTTP.BROWSER" apprisk=elevated applist="default"}
{123.123.123.123, 222.222.222.222, 80, tcp/8080, "Microsoft.Portal"
124.124.124.124, 111.111.111.111, 90, tcp/9090, "HTTP.BROWSER"}
请注意,行大小可能不同,即某些行可能包含更多字段,其他行可能不包含所有字段,即可能不包含
app=
将以下脚本另存为,例如script.sh
$ cat script.sh
#!/usr/bin/env bash
# add all the keys you need to extract here
keys=(srcip dstip dstport service app)
output=""
while read line; do
newline=""
for opt in ${keys[@]}; do
val="$(echo "$line" | sed -n "s/.*${opt}=\(\S*\).*/\1/p;")"
if ! [[ -z $val ]]; then
newline+="$val, "
fi
done
if ! [[ -z $newline ]]; then
output+="${newline::-2}\n"
fi
done <file
if [[ -z $output ]]; then
echo "nothing extracted!"
exit 1
fi
echo -e "{${output::-2}}" > extracted.txt
执行提供输入文件作为第一个参数的脚本:
$ bash script.sh input.txt
这将在工作目录中生成输出文件extracted.txt
输出文件内容:
$ cat input.txt
{date=2017-01-01 time=23:59:59 logid=0000000001 srcip=123.123.123.123 srcport=2222 srcintf="Branches_Out" dstip=222.222.222.222 dstport=80 service="tcp/8080" appid=41469 app="Microsoft.Portal" apprisk=elevated applist="default"
date=2017-01-01 time=24:00:00 logid=0000000002 srcip=124.124.124.124 srcport=3333 srcintf="Branches_Out" dstip=111.111.111.111 dstport=90 service="tcp/9090" appid=15893 app="HTTP.BROWSER" apprisk=elevated applist="default"}
$ cat extracted.txt
{123.123.123.123, 222.222.222.222, 80, "tcp/8080", "Microsoft.Portal"
124.124.124.124, 111.111.111.111, 90, "tcp/9090", "HTTP.BROWSER"}
将以下脚本另存为,例如,
script.sh
$ cat script.sh
#!/usr/bin/env bash
# add all the keys you need to extract here
keys=(srcip dstip dstport service app)
output=""
while read line; do
newline=""
for opt in ${keys[@]}; do
val="$(echo "$line" | sed -n "s/.*${opt}=\(\S*\).*/\1/p;")"
if ! [[ -z $val ]]; then
newline+="$val, "
fi
done
if ! [[ -z $newline ]]; then
output+="${newline::-2}\n"
fi
done <file
if [[ -z $output ]]; then
echo "nothing extracted!"
exit 1
fi
echo -e "{${output::-2}}" > extracted.txt
执行提供输入文件作为第一个参数的脚本:
$ bash script.sh input.txt
这将在工作目录中生成输出文件extracted.txt
输出文件内容:
$ cat input.txt
{date=2017-01-01 time=23:59:59 logid=0000000001 srcip=123.123.123.123 srcport=2222 srcintf="Branches_Out" dstip=222.222.222.222 dstport=80 service="tcp/8080" appid=41469 app="Microsoft.Portal" apprisk=elevated applist="default"
date=2017-01-01 time=24:00:00 logid=0000000002 srcip=124.124.124.124 srcport=3333 srcintf="Branches_Out" dstip=111.111.111.111 dstport=90 service="tcp/9090" appid=15893 app="HTTP.BROWSER" apprisk=elevated applist="default"}
$ cat extracted.txt
{123.123.123.123, 222.222.222.222, 80, "tcp/8080", "Microsoft.Portal"
124.124.124.124, 111.111.111.111, 90, "tcp/9090", "HTTP.BROWSER"}
你可以用几种不同的方法来做你正在尝试的事情。坚持使用一个简单的
grep-Po
将想要的值分离成label=value
格式,然后在读取循环控制IFS
时,通过添加一个=
作为分隔符,将label=value
行管道连接到,然后允许您使用一个简单的计数器(对于您的5个术语,计算到5
),您可以按照所示对其进行格式化
一个简单的脚本可以是:
#!/bin/bash
fname="$1"
test -r "$fname" || { ## validate filename is readable
printf "error: file not readable.\nusage: %s filename\n" "${0//*\//}"
exit 1
}
## use grep -Po to parse into label=value lines
grep -Po 'srcip=[0-9]+[.][0-9]+[.][0-9]+[.][0-9]+|dstip=[0-9]+[.][0-9]+[.][0-9]+[.][0-9]+|dstport=[0-9]+|service="([a-z]+/[0-9]+)"|app="([A-Za-z]+[.][A-Za-z]+)"' "$fname" |
{
beg=0
cnt=0 ## use read with IFS and a counter to parse into CSV
while IFS="$IFS=" read -r label value; do
[ "$beg" -eq '1' ] && [ "$cnt" -eq '0' ] && printf "\n"
[ "$beg" -eq '0' ] && [ "$cnt" -eq '0' ] && { beg=1; printf "{"; }
[ "$cnt" -eq '4' ] && printf "%s" "$value" || printf "%s, " "${value//\"/}"
((cnt++))
((cnt == 5)) && cnt=0
done
printf "}\n"
}
输入文件
都在一行上,但在这里分开显示
$ cat zz
{date=2017-01-01 time=23:59:59 logid=0000000001 srcip=123.123.123.123 srcport=2222
srcintf="Branches_Out" dstip=222.222.222.222 dstport=80 service="tcp/8080"
appid=41469 app="Microsoft.Portal" apprisk=elevated applist="default"
date=2017-01-01 time=24:00:00 logid=0000000002 srcip=124.124.124.124 srcport=3333
srcintf="Branches_Out" dstip=111.111.111.111 dstport=90 service="tcp/9090"
appid=15893 app="HTTP.BROWSER" apprisk=elevated applist="default"}
示例使用/输出
$ bash parselog.sh zz
{123.123.123.123, 222.222.222.222, 80, tcp/8080, "Microsoft.Portal"
124.124.124.124, 111.111.111.111, 90, tcp/9090, "HTTP.BROWSER"}
仔细看一看,让我知道这是否接近你想要达到的目标。你可以用几种不同的方法来做你正在尝试的事情。坚持一个简单的grep-Po
将想要的值分离成label=value
格式,然后在读取循环c时将label=value
行输送到通过添加一个=
作为分隔符来控制IFS
,然后允许您使用一个简单的计数器(对5个术语的5
),您可以按照所示对其进行格式化
一个简单的脚本可以是:
#!/bin/bash
fname="$1"
test -r "$fname" || { ## validate filename is readable
printf "error: file not readable.\nusage: %s filename\n" "${0//*\//}"
exit 1
}
## use grep -Po to parse into label=value lines
grep -Po 'srcip=[0-9]+[.][0-9]+[.][0-9]+[.][0-9]+|dstip=[0-9]+[.][0-9]+[.][0-9]+[.][0-9]+|dstport=[0-9]+|service="([a-z]+/[0-9]+)"|app="([A-Za-z]+[.][A-Za-z]+)"' "$fname" |
{
beg=0
cnt=0 ## use read with IFS and a counter to parse into CSV
while IFS="$IFS=" read -r label value; do
[ "$beg" -eq '1' ] && [ "$cnt" -eq '0' ] && printf "\n"
[ "$beg" -eq '0' ] && [ "$cnt" -eq '0' ] && { beg=1; printf "{"; }
[ "$cnt" -eq '4' ] && printf "%s" "$value" || printf "%s, " "${value//\"/}"
((cnt++))
((cnt == 5)) && cnt=0
done
printf "}\n"
}
输入文件
都在一行上,但在这里分开显示
$ cat zz
{date=2017-01-01 time=23:59:59 logid=0000000001 srcip=123.123.123.123 srcport=2222
srcintf="Branches_Out" dstip=222.222.222.222 dstport=80 service="tcp/8080"
appid=41469 app="Microsoft.Portal" apprisk=elevated applist="default"
date=2017-01-01 time=24:00:00 logid=0000000002 srcip=124.124.124.124 srcport=3333
srcintf="Branches_Out" dstip=111.111.111.111 dstport=90 service="tcp/9090"
appid=15893 app="HTTP.BROWSER" apprisk=elevated applist="default"}
示例使用/输出
$ bash parselog.sh zz
{123.123.123.123, 222.222.222.222, 80, tcp/8080, "Microsoft.Portal"
124.124.124.124, 111.111.111.111, 90, tcp/9090, "HTTP.BROWSER"}
仔细检查一下,让我知道这是否接近您想要实现的目标。您可以使用Perl正则表达式来匹配整个模式
Pattern='{date=(.*?) time=(.*?) logid=(.*?) srcip=(.*?) srcport=(.*?) srcintf=(.*?) dstip=(.*?) dstport=(.*?) service=(.*?) appid=(.*?) app=(.*?) apprisk=(.*?) applist=(.*?)';
然后,在显示时,您可以使用匹配号($1、$2…)替换匹配字符
perl-spe'print s/$Pattern/{$4、$7、$8、$9、$11}/g'
您需要在替换中添加其他匹配号。您可以使用Perl正则表达式匹配整个模式
Pattern='{date=(.*?) time=(.*?) logid=(.*?) srcip=(.*?) srcport=(.*?) srcintf=(.*?) dstip=(.*?) dstport=(.*?) service=(.*?) appid=(.*?) app=(.*?) apprisk=(.*?) applist=(.*?)';
然后,在显示时,您可以使用匹配号($1、$2…)替换匹配字符
perl-spe'print s/$Pattern/{$4、$7、$8、$9、$11}/g'
您需要在替换中添加其他匹配号。在awk中:
$ awk '
BEGIN { # in the beginning
split("srcip dstip dstport service app",t) # form wanted keyword list
for(i in t)
a[t[i]]
}
{
for((i=1)&&b="";i<=NF;i++) { # check every field
split($i,k,"=") # split on =
if(k[1] in a) # if in keyword list
b=b (b==""?(NR==1?"{":"\n"):OFS) k[2] # append to buffer
}
printf "%s", b # output buffer
}
END {
print "}" # sugar on the top
}' file
{123.123.123.123 222.222.222.222 80 "tcp/8080" "Microsoft.Portal"
124.124.124.124 111.111.111.111 90 "tcp/9090" "HTTP.BROWSER"}
$awk'
从一开始就开始
拆分(“srcip-dstip-dsport服务应用程序”,t)#表格通缉关键字列表
for(i in t)
a[t[i]]
}
{
对于awk中的((i=1)和&b=“”;i:
$ awk '
BEGIN { # in the beginning
split("srcip dstip dstport service app",t) # form wanted keyword list
for(i in t)
a[t[i]]
}
{
for((i=1)&&b="";i<=NF;i++) { # check every field
split($i,k,"=") # split on =
if(k[1] in a) # if in keyword list
b=b (b==""?(NR==1?"{":"\n"):OFS) k[2] # append to buffer
}
printf "%s", b # output buffer
}
END {
print "}" # sugar on the top
}' file
{123.123.123.123 222.222.222.222 80 "tcp/8080" "Microsoft.Portal"
124.124.124.124 111.111.111.111 90 "tcp/9090" "HTTP.BROWSER"}
$awk'
从一开始就开始
拆分(“srcip-dstip-dsport服务应用程序”,t)#表格通缉关键字列表
for(i in t)
a[t[i]]
}
{
对于((i=1)和&b=“”;如果所有行以相同的顺序具有相同的格式术语(5),则iIt可以正常工作,但如果行不是静态的,则如何使其工作,即行可能包含或可能不包含所有5个术语,术语也可能不具有相同的顺序。这将成为一个不同的问题。可以这样做,但必须包含另一个循环,并逐字读取输入。(或者从一组变量术语动态构建正则表达式),然后在一个单独的循环中,根据正则表达式集扫描每个单词以检查是否匹配。如果找到匹配项,则可以将其添加到每行开头未设置的数组中(因此只保存每行找到的匹配项)扫描行中的每个单词后,您可以将数组的内容以CSV格式写出来(处理数量可变的术语)。如果所有行都具有相同的格式术语,则效果良好(5)以相同的顺序,但如果行不是静态的,如何使其工作,即行可能包含也可能不包含所有5个术语,术语也可能不以相同的顺序。这将成为一个不同的问题。可以这样做,但必须包含另一个循环,并逐字读取输入。(或从一组可变术语动态构建正则表达式)然后在一个单独的循环中,根据正则表达式集扫描每个单词以检查是否匹配。如果找到匹配项,则可以将其添加到每行开头未设置的数组中(因此仅保存每行找到的匹配项),然后在扫描行中的每个单词后,可以CSV格式写出数组的内容(处理可变数量的术语)不起作用,似乎val=“$(echo“$line”| sed-n“s/*${opt}=(\s*)./\1/p;”)不正确可能是您做错了什么。我已经用逐步详细信息更新了答案。如果问题仍然存在,请提供您所遇到错误的确切细节。工作正常,Thanx a lot不起作用,似乎val=”$(echo“$line”| sed-n“s/*${opt}=(\s*)./\1/p;”)不正确可能是您做错了什么。我已经用逐步详细的信息更新了答案。如果问题仍然存在,请提供有关您得到的错误的确切详细信息。工作正常,Thanx一个错误行:b=b(b==“”?(NR==1?“{”):OFS)k[2]错误是什么?看看它,“\n”
应该替换为ORS
。awk:cmd.line:11:调用未定义的函数那么你在(
)前面缺少一个空格;它试图调用不存在的函数b()
或?()
。它的工作非常神奇,我已经在另一个系统及其ru上运行过它