【shell二三轶事】如何取出两个文本中相同和不同的部分?

【前言】

这个问题也是工作中遇到的,当时我的领导让我20分钟写出来。我原以为很容易的,结果竟然意料之外的难写啊。

具体问题是这样的:现在有两个行数比较大的文本文件a.txt和b.txt,请输出三个文本a_only.txt, b_only.txt, both.txt。

【正文】

1. 取出不同部分

这个部分相对来说比较好做,只需要用到diff命令就可以解决。 但是要注意,要先 排序并且去重 。 为什么要排序并且去重呢? 要说明这个问题,咱们先来看看diff命令的效果。

1.1 diff命令

工作原理:顺序遍历左边文件的每一行,去对比右边文件的每一行。每次比较操作过后,左边文件就会进入下一行,而右边文件只有在和左边文件相同的情况下,才会进入下一行。 举个例子:

a.txt
b.txt
执行 diff a.txt b.txt
它的具体步骤是:
比较c和a,发现c和a不同,则1d0,表示a.txt的第1行是比b.txt多出的内容,且位置是在b.txt的第0行后面
比较a和a,发现a和a相同,则什么也不做
比较b和c,发现b和c不同,则3c2,表示a.txt的第3行和b.txt的第2行不同
实际的输出:
> c

我们改变一下a.txt和b.txt的顺序,让b.txt在前面。

b.txt
a.txt
执行 diff b.txt a.txt
它的具体步骤是:
比较a和c,发现a和c不同,则1d0。意思是b.txt的第1行是比a.txt多出的内容,且位置是在a.txt的第0行后面
比较c和c,发现c和c相同,则什么也不做
b.txt文本已经没东西了,但是a.txt还有两行,则2a2,3。意思是在b.txt的第2行后面比a.txt的少了内容,且少的是a.txt的第2和第3行
实际的输出:
2a2,3
> b

我们可以看到,如果不进行排序和去重,diff命令的结果并不是我们想要的。明明c是两边都有的,但还是筛选出来。 我们现在看看加了排序和去重之后,会有什么效果呢?

a.txt
b.txt
< b

这样做,a.txt独有的b便筛选出来了,我们便达到了我们的目的。

1.2 具体脚本

sort -u a.txt > a_tmp.txt
sort -u b.txt > b_tmp.txt
diff a_tmp.txt b_tmp.txt | grep -E "< .+" | sed "s/^..//g" > a_only.txt
diff a_tmp.txt b_tmp.txt | grep -E "> .+" | sed "s/^..//g" > b_only.txt
rm -rf a_tmp.txt
rm -rf b_tmp.txt

2. 取出相同部分

主要思想:先把两个文件各自去重,然后合起来进行排序。遍历合起来的文件,如果当前行和前一行相同,就是相同部分。

具体脚本:

uniq a.txt > a_tmp.txt
uniq b.txt > b_tmp.txt
cp a_tmp.txt all.txt
cat b_tmp.txt >> all.txt
rm -rf a_tmp.txt
rm -rf b_tmp.txt
pre="error"
while read line
    if [ x$line = x$pre ]