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

因为工作原因,经常经统计一些文本内容,要去除重复,之前都是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

你可能感兴趣的