![显示多个文件中重复行的位置](https://linux22.com/image/1205805/%E6%98%BE%E7%A4%BA%E5%A4%9A%E4%B8%AA%E6%96%87%E4%BB%B6%E4%B8%AD%E9%87%8D%E5%A4%8D%E8%A1%8C%E7%9A%84%E4%BD%8D%E7%BD%AE.png)
我有两个文件:
#a.txt
11
22
33
44
55
11
和
# b.txt
55
66
77
88
99
11
我执行这个命令来打印两个文件中的重复行:sort *.txt | uniq -D
并且它输出:
11
11
11
55
55
我希望它输出准确的位置;例如,对于输出的第 1 行,它应该说明它来自哪里:第 1 行,文件 a.txt即一种差异。
请问我该如何做到这一点?
答案1
总结
你可以做这样的事情:
file_expr="*.txt"; sort $file_expr | sed 's/^\s*//; s/\s*$//; /^\s*$/d' | uniq -d | while read dup_line; do grep -Hn "^\s*$dup_line\s*$" $file_expr; done| sort -t: -k3 -k1,2 | awk -F: '{ file=$1; line=$2; $1=$2=""; gsub(/(^[ \t]+)|([ \t]+$)/,"",$0); if (prev != "" && prev != $0) printf ("\n"); printf ("\033[0;33m%s (line %s)\033[0m: %s\n", file, line, $0); prev=$0; }'
结果:
a.txt (line 3): 11
a.txt (line 8): 11
b.txt (line 8): 11
a.txt (line 7): 55
b.txt (line 3): 55
改变变量的内容file_expr
来改变受影响的文件
解释
我使用sed
命令来修剪全部尾随和领导空格和删除空行,使得uniq -d
仅获取真正重复的行......
然后我环形重复的行(每匹配一次打印一行)并grep
使用标志将它们放入文件中-n
(打印文件和线) 和-H
(总是显示文件名)。表达式^\s*$dup_line\s*$
放入grep
使其匹配全线(例如“qwerty11uiop”不匹配)。
正如你所看到的,它可以使用文件通配符...
file_expr="*.txt"; sort $file_expr | sed 's/^\s*//; s/\s*$//; /^\s*$/d' | uniq -d | while read dup_line; do grep -Hn "^\s*$dup_line\s*$" $file_expr; done
结果:
a.txt:3:11
a.txt:8:11
b.txt:8:11
a.txt:7:55
b.txt:3:55
... 和文字文件名..
file_expr="a.txt b.txt"; sort $file_expr | sed 's/^\s*//; s/\s*$//; /^\s*$/d' | uniq -d | while read dup_line; do grep -Hn "^\s*$dup_line\s*$" $file_expr; done
结果:
a.txt:3:11
a.txt:8:11
b.txt:8:11
a.txt:7:55
b.txt:3:55
小调整
然后我稍微调整了一下,让它看起来更舒服的... 像这样:
file_expr="a.txt b.txt"; sort $file_expr | sed 's/^\s*//; s/\s*$//; /^\s*$/d' | uniq -d | while read dup_line; do grep -Hn "^\s*$dup_line\s*$" $file_expr; done| sort -t: -k3 -k1,2 | awk -F: '{ file=$1; line=$2; $1=$2=""; gsub(/(^[ \t]+)|([ \t]+$)/,"",$0); if (prev != "" && prev != $0) printf ("\n"); printf ("\033[0;33m%s (line %s)\033[0m: %s\n", file, line, $0); prev=$0; }'
结果:
a.txt (line 3): 11
a.txt (line 8): 11
b.txt (line 8): 11
a.txt (line 7): 55
b.txt (line 3): 55
在最后一个视图中,一切都更加“人性化”,重复项首先按结果分组,然后按文件分组(您可以看到结果a.txt
都在一起),因此更容易理解。
文件名和行现在为黄色(\033[0;33m
),以区别于实际行中的文本,以防出现多行(请原谅这个双关语)重复的情况
答案2
由于您的示例中的b.txt
文件中含有空格,因此您可以使用它awk
来抓取空格前的字符。
将我上面的评论转换为此处的答案。您可以使用grep -n
显示行号及其来源文件。还要添加一个uniq
命令,以便当 grep 循环查找重复项时,它不会经过 3 个 11 和 2 个 55。
简单示例:
sort *.txt | awk '{print $1}' | uniq -D
11
11
11
55
55
通过添加第二个,uniq
输出仅为:
11
55
现在我们可以添加grep
和while
循环。 grep
将显示每个出现的次数以及它们位于哪个文件中。
sort *.txt | awk '{print $1}' | uniq -D | uniq | while read num; do grep -n $num *.txt; done
例子:
$ sort *.txt | awk '{print $1}' | uniq -D | uniq | while read num; do grep -n $num *.txt; done
a.txt:2:11
a.txt:7:11
b.txt:7:11
a.txt:6:55
b.txt:2:55