Linux 删除重复的行

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

我需要一些帮助来转换损坏的日志,我从管理员那里得到了帮助(不幸的是他们不会修复它)。 基本上,有时日志包含重复的第一列,如果另一列的值等于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.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所说的那样安全