美文网首页生产力
终于明白awk如何牛掰地不排序也能去掉文件里的重复行了

终于明白awk如何牛掰地不排序也能去掉文件里的重复行了

作者: 五大RobertWu伍洋 | 来源:发表于2017-01-05 16:03 被阅读483次

    因为工作原因,经常经统计一些文本内容,要去除重复,之前都是cat xx | sort | uniq 类的命令。

    自从开始关注使用VIM之后,发现VIM真的很强大,直接有去重功能,怪不得这么多Linuxer都喜欢它。

    我这里只说它的去重功能首先讲点基础,

    awk流程是逐行处理的,默认从文件的第一行一直处理到文件最后一行,

    还要知道awk的基本命令格式是'pattern{action}'先匹配各种各样的样式,然后大括号里处理如何打印输出,

    默认的只要匹配了pattern就{print $0},如果pattern未命中其判断值为假(0)那么就不会再去处理{action}了

    pattern命中则为判断值为真(非0)就去处理{action}。

    以下操作都是在vim命令模式下的操作

    复制内容到剪贴板

    代码:

    : sort //可以直接排序,这个太好用了

    :g/^\(.*\)$\n\1$/d    //去除重复行

    :g/\%(^\1$\n\)\@<=\(.*\)$/d //功能同上,也是去除重复行

    :g/\%(^\1\>.*$\n\)\@<=\(\k\+\).*$/d//功能同上,也是去除重复行

    其实都是一些正则,但是本人正则不太好,也真的解释不清,希望有大牛能详细的解释下,每个正则是神马意思!

    因为工作原因,经常经统计一些文本内容,要去除重复,之前都是cat xx | sort | uniq 类的命令,自从开始关注使用VIM之后,发现VIM真的很强大,直接有去重功能,怪不得这么多Linuxer都喜欢它。我这里只说它的去重功能

    以下操作都是在命令模式下的操作

    复制内容到剪贴板

    代码:

    : sort //可以直接排序,这个太好用了

    :g/^\(.*\)$\n\1$/d    //去除重复行

    :g/\%(^\1$\n\)\@<=\(.*\)$/d //功能同上,也是去除重复行

    :g/\%(^\1\>.*$\n\)\@<=\(\k\+\).*$/d//功能同上,也是去除重复行

    其实都是一些正则,但是本人正则不太好,也真的解释不清,希望有大牛能详细的解释下,每个正则是神马意思!

    -------------------------------------------------------------------------------------

    awk '!a[$0]++'

    而如果使用sort加uniq进行排序的话,这个文档是看不出有什么不妥。

    不过我要处理的是用户名与密码一行行对应好的,如果使用sort + uniq处理的话,用户名都排到一块了,密码也又都跑到一块了。

    这样就分不出来那个是那个了。

    而使用的脚本很简单:

    awk '!x[$0]++' filename

    注:此处的x只是一个数据参数的名字而已,随你用a、b、c、d都行。

    简要解释一下,awk 的基本执行流程是,对文件的每一行,做一个指定的逻辑判断,如果逻辑判断成立,则执行指定的命令;如果逻辑判断不成立,则直接跳过这一行。

    我们这里写的 awk 命令是!x[$0]++,意思是:

    首先创建一个 map 叫x,然后用当前行的全文$0作为 map 的 key,到 map 中查找相应的 value,如果没找到,则整个表达式的值为真,可以执行之后的语句;

    如果找到了,则表达式的值为假,跳过这一行。

    由于表达式之后有++,因此如果某个 key 找不到对应的 value,该++操作会先把对应的 value 设成 0,然后再自增成 1,这样下次再遇到重复的行的时候,对应的 key 就能找到一个非 0 的 value 了。

    注:该处的map类似于array数组,只不过在awk中叫array不恰当。

    awk Oneline中我们也学到过,awk 的流程是先判断表达式,表达式为真的时候就执行语句,可是我们前面写的这个 awk 命令里只有表达式,没有语句,那我们执行什么呢?原来,当语句被省略的时候,awk 就执行默认的语句,即打印整个完整的当前行。就这样,我们通过这个非常简短的 awk 命令实现了去除重复行并保留原有文件顺序的功能。

    -------------------------------------------------------------------------------------

    awk '!x[$0]++' shareprofile.txt > shareprofile_awk.txt

    -------------------------------------------------------------------------------------

    举个最简单的例子:awk '1' file和awk '{print $0}' file是一个道理,都是从头到尾依次打印文件的每一行

    基础知识就这些,如果底太潮去man awk或者google找吧。

    '!a[$0]++'

    分成几个部分简单解释下吧

    这个命令没有{action}也就是说,只要pattern部分判断值为真(非0)就打印正行,否则就跳过不打印

    !在awk是取相反的意思,就是把对的变成错的把真的变成假的,放在这个命令中是神马作用一会解释

    a[$0]这个非常好理解,建立数组a其变量是文本中的每一行,awk里$1是第一列,$2是第二列,以此类推$NF是最后一列,而$0是代表所有列及分隔符,也就是一整行,这样如果pattern是真的那就打印一整行

    ++的意思是a数组取变量完毕后,对该数组值+1

    找个最简单的文档来解释一下

    复制内容到剪贴板

    代码:

    cat file

    xxx

    yyy

    xxx

    zzz

    这个文件有4行,其中第一、三行是重复的。套用这个命令处理流程如下

    获取第一行a[xxx],因为这是第一行,数组a里从没见过xxx这个变量,那么自然他的值就是假(0)也就是说a[xxx]=0,这个时候!就有大作用了,他把a[xxx]假(0)变成了a[xxx]为真(!0)这个时候原本不改打印的第一行就变成了应该打印了,取逻辑反后对a[xxx]的值+1然后处理第二行

    第二行a[yyy]这个情况跟刚才第一行的a[xxx]一样,也应该打印他

    到第三行的时候情况遍了,因为第一行已经出现过了a[xxx]并且已经++过了他的值已经是非0而不是前两行的0了,本应打印但这时候再由!取逻辑反就不必打印了

    第四行a[zzz]就又和第一、二两行一样了。

    所以执行完就是这个结果

    awk '!a[$0]++' file

    xxx

    yyy

    zzz

    再把file搞稍微复杂点

    复制内容到剪贴板

    代码:

    awk '{print NR,$0}' file

    1 xxx

    2 yyy

    3 zzz

    4 xxx

    5 yyy

    6 zzz

    7 xxx

    8 yyy

    9 zzz

    一共9行文本,3行一次重复。为了看得更清楚,本来默认的{print $0}稍微改下,变成{print NR(行号),$0}。

    那么现在来执行下刚才讲的试试看

    复制内容到剪贴板

    代码:

    awk '!a[$0]++{print NR,$0}' file

    1 xxx

    2 yyy

    3 zzz

    复制内容到剪贴板

    代码:

    awk 'a[$0]++{print NR,$0}' file

    4 xxx

    5 yyy

    6 zzz

    7 xxx

    8 yyy

    9 zzz

    很明显了吧,有!的命令是只打印第一次出现的$0也就是去除重复咯,而没有!的命令正好跟他相反,就是仅仅去除第一次出现的$0

    相关文章

      网友评论

        本文标题:终于明白awk如何牛掰地不排序也能去掉文件里的重复行了

        本文链接:https://www.haomeiwen.com/subject/cbyzvttx.html