Linux 删除重复的行
我需要一些帮助来转换损坏的日志,我从管理员那里得到了帮助(不幸的是他们不会修复它)。 基本上,有时日志包含重复的第一列,如果另一列的值等于null,我希望删除该列 这就是它看起来的样子Linux 删除重复的行,linux,bash,awk,Linux,Bash,Awk,我需要一些帮助来转换损坏的日志,我从管理员那里得到了帮助(不幸的是他们不会修复它)。 基本上,有时日志包含重复的第一列,如果另一列的值等于null,我希望删除该列 这就是它看起来的样子 datetime,auth_protocol,result,client,port,login 2016-07-15 09:34:52,www,PASS,111.222.333.444,5593,someAddress@domain.com 2016-07-15 09:34:52,www,PASS,111.222
datetime,auth_protocol,result,client,port,login
2016-07-15 09:34:52,www,PASS,111.222.333.444,5593,someAddress@domain.com
2016-07-15 09:34:52,www,PASS,111.222.333.444,NULL,someAddress@domain.com
2016-08-14 00:51:35,www,PASS,NULL,19509,someAddress@domain.com
2016-08-14 00:51:35,www,PASS,444.333.222.111,19509,someAddress@domain.com
2016-09-16 05:08:46,www,FAIL,123.456.789.222,45673,someAddress@domain.com
2016-09-16 15:35:55,www,FAIL,123.456.789.222,NULL,someAddress@domain.com
2016-09-17 17:10:01,www,FAIL,111.222.333.444,55516,someAddress@domain.com
如您所见,有些行复制了第一列(日期)
我想做的是:
1:对于所有列,第一列重复,比较4列或5列(IP或端口)
2:删除第4列或第5列中具有空值的重复行
这应该是正确的输出:
datetime,auth_protocol,result,client,port,login
2016-07-15 09:34:52,www,PASS,111.222.333.444,5593,someAddress@domain.com
2016-08-14 00:51:35,www,PASS,444.333.222.111,19509,someAddress@domain.com
2016-09-16 05:08:46,www,FAIL,123.456.789.222,45673,someAddress@domain.com
2016-09-16 15:35:55,www,FAIL,123.456.789.222,NULL,someAddress@domain.com
2016-09-17 17:10:01,www,FAIL,111.222.333.444,55516,someAddress@domain.com
我希望这听起来很清楚;你可以用这个awk 在文件上迭代两次。
第一次重复计数。
第二行不重复或不包含NULL的打印行
awk -F, 'NR==FNR{a[$1]++;next}a[$1]<2||$4$5!~/NULL/' file{,}
datetime,auth_protocol,result,client,port,login
2016-07-15 09:34:52,www,PASS,111.222.333.444,5593,someAddress@domain.com
2016-08-14 00:51:35,www,PASS,444.333.222.111,19509,someAddress@domain.com
2016-09-16 05:08:46,www,FAIL,123.456.789.222,45673,someAddress@domain.com
2016-09-16 15:35:55,www,FAIL,123.456.789.222,NULL,someAddress@domain.com
2016-09-17 17:10:01,www,FAIL,111.222.333.444,55516,someAddress@domain.com
awk-F,'NR==FNR{a[$1]+;next}a[$1]您可以使用此awk
在文件上迭代两次。
第一次重复计数。
第二行不重复或不包含NULL的打印行
awk -F, 'NR==FNR{a[$1]++;next}a[$1]<2||$4$5!~/NULL/' file{,}
datetime,auth_protocol,result,client,port,login
2016-07-15 09:34:52,www,PASS,111.222.333.444,5593,someAddress@domain.com
2016-08-14 00:51:35,www,PASS,444.333.222.111,19509,someAddress@domain.com
2016-09-16 05:08:46,www,FAIL,123.456.789.222,45673,someAddress@domain.com
2016-09-16 15:35:55,www,FAIL,123.456.789.222,NULL,someAddress@domain.com
2016-09-17 17:10:01,www,FAIL,111.222.333.444,55516,someAddress@domain.com
awk-F,'NR==FNR{a[$1]+;next}a[$1]通过对日志文件进行两次迭代,可以收集第一次迭代中使用的所有日期。如果日期已在第二次迭代中使用,则删除空日志:
$ awk -F, '$4$5 !~ /NULL/ {d[$1]=1}
NR == FNR { next }
!d[$1] || $4$5 !~ /NULL/' input.log input.log
第一次迭代d
将使用指定字段中不包含NULL
的行中的已用日期填充
NR==FNR
仅对迭代的第一个文件为真
在第二次迭代中,如果第4列或第5列中不包含NULL
,则将打印行。或者,如果d
不包含使用的日期。通过对日志文件进行两次迭代,您可以在第一次迭代中收集所有使用的日期。如果日期已在第二次迭代中使用,则删除空日志:
$ awk -F, '$4$5 !~ /NULL/ {d[$1]=1}
NR == FNR { next }
!d[$1] || $4$5 !~ /NULL/' input.log input.log
第一次迭代d
将使用指定字段中不包含NULL
的行中的已用日期填充
NR==FNR
仅对迭代的第一个文件为真
在第二次迭代中,如果第4列或第5列中不包含NULL
,则将打印行。或者,如果d
不包含使用的日期。您可以使用bash中的短脚本解决方案,使用数组一次性完成。(根据文件的长度,您可能会发现使用awk
的双通道速度更快,但对于较大的文件,此版本可能更有利)。脚本只读取所有行,但延迟打印每行,直到读取下一行并比较日期/时间,以确定是否存在可能的重复条目。如果日期/时间相等,则检查IP字段。如果IP为NULL
它将跳过打印该行
这只是许多方法中的一种。尝试一下您的数据:
编辑在id
字段中为NULL
添加测试
#!/bin/bash
fn="${1:-/dev/stdin}" ## read file or stdin
prevln="" ## previous line & prev and curr arrays of values
declare -a prev=( ' ' ' ' ' ' ' ' ' ' ' ' ' ' )
declare -a curr
declare -i iddup=0 ## flag marking duplicate in 'id' field
IFS=$' ,\t\n' ## add ',' to internal field separator
while read -r line || test -n "$line"; do ## read each line
curr=( $(echo $line) ) ## fill current array with values
## test prev date/time with curr date/time
if [ "${prev[0]}" = "${curr[0]}" -a "${prev[1]}" = "${curr[1]}" ]; then
if [ "${prev[4]}" != "NULL" ]; then ## if prev IP != NULL print
echo "$prevln" ## otherwise, skip print
fi
[ "${curr[5]}" = "NULL" ] && iddup=1 || iddup=0 ## flag id dup
elif [ "$iddup" -eq '0' ]; then ## if date/time inequal, print line
[ -n "$prevln" ] && echo "$prevln"
fi
prevln="$line" ## assign line to prevln
prev=( ${curr[@]} ) ## assign curr to prev array
done <"$fn"
## same test for the last line after loop exits
curr=( $(echo $line) )
if [ "${prev[0]}" = "${curr[0]}" -a "${prev[1]}" = "${curr[1]}" ]; then
if [ "${prev[4]}" = "NULL" ]; then
echo "$line"
elif [ "${curr[4]}" = "NULL" ]; then
echo "$prevln"
else
echo "$prevln"
fi
else ## if date/time inequal, print line
[ -n "$prevln" ] && echo "$prevln"
fi
示例使用/输出
$ bash logdups.sh <dat/log.txt
2016-07-15 09:34:52,www,PASS,111.222.333.444,5593,someAddress@domain.com
2016-07-15 00:51:35,www,PASS,444.333.222.111,19509,someAddress@domain.com
2016-09-16 05:08:46,www,FAIL,123.456.789.222,45673,someAddress@domain.com
2016-09-16 15:35:55,www,FAIL,123.456.789.222,NULL,someAddress@domain.com
2016-09-17 17:10:01,www,FAIL,111.222.333.444,55516,someAddress@domain.com
$bash logdups.sh您可以使用bash中的短脚本解决方案,利用数组一次性完成此任务。(根据文件的长度,您可能会发现使用awk
的双通道速度更快,但对于较大的文件,此版本可能更有利)。脚本只读取所有行,但延迟打印每行,直到读取下一行并比较日期/时间,以确定是否存在可能的重复条目。如果日期/时间相等,则检查IP字段。如果IP为NULL
它将跳过打印该行
这只是许多方法中的一种。尝试一下您的数据:
编辑在id
字段中为NULL
添加测试
#!/bin/bash
fn="${1:-/dev/stdin}" ## read file or stdin
prevln="" ## previous line & prev and curr arrays of values
declare -a prev=( ' ' ' ' ' ' ' ' ' ' ' ' ' ' )
declare -a curr
declare -i iddup=0 ## flag marking duplicate in 'id' field
IFS=$' ,\t\n' ## add ',' to internal field separator
while read -r line || test -n "$line"; do ## read each line
curr=( $(echo $line) ) ## fill current array with values
## test prev date/time with curr date/time
if [ "${prev[0]}" = "${curr[0]}" -a "${prev[1]}" = "${curr[1]}" ]; then
if [ "${prev[4]}" != "NULL" ]; then ## if prev IP != NULL print
echo "$prevln" ## otherwise, skip print
fi
[ "${curr[5]}" = "NULL" ] && iddup=1 || iddup=0 ## flag id dup
elif [ "$iddup" -eq '0' ]; then ## if date/time inequal, print line
[ -n "$prevln" ] && echo "$prevln"
fi
prevln="$line" ## assign line to prevln
prev=( ${curr[@]} ) ## assign curr to prev array
done <"$fn"
## same test for the last line after loop exits
curr=( $(echo $line) )
if [ "${prev[0]}" = "${curr[0]}" -a "${prev[1]}" = "${curr[1]}" ]; then
if [ "${prev[4]}" = "NULL" ]; then
echo "$line"
elif [ "${curr[4]}" = "NULL" ]; then
echo "$prevln"
else
echo "$prevln"
fi
else ## if date/time inequal, print line
[ -n "$prevln" ] && echo "$prevln"
fi
示例使用/输出
$ bash logdups.sh <dat/log.txt
2016-07-15 09:34:52,www,PASS,111.222.333.444,5593,someAddress@domain.com
2016-07-15 00:51:35,www,PASS,444.333.222.111,19509,someAddress@domain.com
2016-09-16 05:08:46,www,FAIL,123.456.789.222,45673,someAddress@domain.com
2016-09-16 15:35:55,www,FAIL,123.456.789.222,NULL,someAddress@domain.com
2016-09-17 17:10:01,www,FAIL,111.222.333.444,55516,someAddress@domain.com
$bash logdups.sh您可以向我们展示一些您尝试过的代码。重复的行是否总是在日志中直接跟随?是否有过第4列或第5列包含空值而不是重复行的情况?1)是的,始终是2)是的,空值可以像我在正确的example@Inian,我临时做了一些丑陋的东西,但它很丑陋;)cat account.csv | grep-Ff>temp.csv;读行时;do single=$(echo$行| cut-d“,”-f1);如果[[$(cat temp.csv | grep“$single”)==”];然后回显$line>>temp.csv;fi已完成帐户。csv@Wyjun,请将您尝试的代码输入实际问题。您将得到更多的响应。您可以向我们展示一些您尝试的代码。重复的行是否总是直接在日志中彼此跟随?是否有过第4列或第5列包含空值而不是重复行的情况?1)是的,始终是2)是的,空值可以像我在正确的example@Inian,我临时做了一些丑陋的东西,但它很丑陋;)cat account.csv | grep-Ff>temp.csv;读行时;do single=$(echo$行| cut-d“,”-f1);如果[[$(cat temp.csv | grep“$single”)==”];然后回显$line>>temp.csv;fi已完成帐户。csv@Wyjun,请将您尝试的代码输入实际问题。您将获得更多响应。如果有两个空行共享同一日期,并且没有关联的有效记录,则此操作将失败。然而,这不一定是一个需要担心的问题。@andlrc只是针对问题要求编写的。如果有两个空行共享同一日期,并且没有关联的有效记录,则此操作将失败。然而,这不一定是一个需要担心的问题。@andlrc只是为了问题需求而写的。哎呀,你为什么抄我的4美元5美元@123我意识到没有理由要安全,因为OP声明这两列包含IP和端口,如果它们带有字母数字列,我永远不会这样做。@andlrc Lol你刚才复制了我的答案,如果我没有发布它,你“意识到”的几率为0%。@123我的shell历史记录中有它。删除它,因为我认为我可以制作一个“更安全”的脚本。然后意识到没有必要。哎呀,你抄我的4美元5美元干嘛@123我意识到没有理由像OP所说的那样安全