我敢肯定,我曾经找到一个unix命令,该命令可以从两个或更多文件中打印公共行,有人知道它的名字吗?它比容易得多diff
。
pr-123-xy-45
file2包含)时在两个文件之间变得通用ec11_orop_pr-123-xy-45.gz
。我需要包含的file3ec11_orop_pr-123-xy-45.gz
我敢肯定,我曾经找到一个unix命令,该命令可以从两个或更多文件中打印公共行,有人知道它的名字吗?它比容易得多diff
。
pr-123-xy-45
file2包含)时在两个文件之间变得通用ec11_orop_pr-123-xy-45.gz
。我需要包含的file3ec11_orop_pr-123-xy-45.gz
Answers:
您要查找的命令是comm
。例如:-
comm -12 1.sorted.txt 2.sorted.txt
这里:
-1:取消第1列(1.sorted.txt特有的行)
-2:取消第2列(2.sorted.txt的唯一行)
grep
会产生一些您可能不会想到的奇怪的事情。具体来说,in中的所有内容都1.txt
将解释为正则表达式,而不是纯字符串。另外,中的任何空白行都1.txt
将匹配中的所有行2.txt
。因此,grep
仅在非常特定的情况下有效。您至少要使用fgrep
(或grep -f
),但空白行可能会在此过程中造成严重破坏。
comm
命令的输出分成3个单独的文件? 答案太大了,无法舒适地放在这里。
要轻松地将comm命令应用于未排序的文件,请使用Bash的进程替换:
$ bash --version
GNU bash, version 3.2.51(1)-release
Copyright (C) 2007 Free Software Foundation, Inc.
$ cat > abc
123
567
132
$ cat > def
132
777
321
因此,文件abc和def具有共同的一行,即“ 132”。在未排序的文件上使用comm:
$ comm abc def
123
132
567
132
777
321
$ comm -12 abc def # No output! The common line is not found
$
最后一行没有输出,没有发现公共行。
现在对已排序的文件使用comm,使用进程替换对文件进行排序:
$ comm <( sort abc ) <( sort def )
123
132
321
567
777
$ comm -12 <( sort abc ) <( sort def )
132
现在我们有了132条线!
sort abc > abc.sorted
,sort dev > def.sorted
然后comm -12 abc.sorted def.sorted
呢?
为了补充Perl单行代码,这里是它的awk
等效内容:
awk 'NR==FNR{arr[$0];next} $0 in arr' file1 file2
这将从file1
阵列中读取所有行arr[]
,然后检查阵列中file2
是否已存在每行(即file1
)。找到的行将按照它们在中出现的顺序进行打印file2
。请注意,比较in arr
使用整行file2
作为索引到数组,因此它将仅报告整行的完全匹配。
perl
)。万分感谢,女士
也许你的意思是comm
?
逐行比较排序后的文件FILE1和FILE2。
如果没有选项,则产生三列输出。第一列包含FILE1独有的行,第二列包含FILE2独有的行,第三列包含两个文件共同的行。
查找这些信息的秘密是信息页面。对于GNU程序,它们比其手册页详细得多。试试看info coreutils
,它将列出所有有用的小实用程序。
而
grep -v -f 1.txt 2.txt > 3.txt
为您提供两个文件的区别(2.txt中的内容而不是1.txt中的内容),您可以轻松地执行
grep -f 1.txt 2.txt > 3.txt
收集所有常见的行,这应该为您的问题提供简单的解决方案。如果您对文件进行了排序,则仍应采取措施comm
。问候!
grep
做一些您可能没有想到的奇怪的事情。具体来说,in中的所有内容都1.txt
将解释为正则表达式,而不是纯字符串。另外,中的任何空白行都1.txt
将匹配中的所有行2.txt
。因此,这仅在非常特殊的情况下有效。
comm
处理分类文件?
comm
可以处理任意大文件,因为它们只需要排序即可,因为它只需要在内存中保留三行(我猜GNU comm
甚至知道如果行真的很长也只能保留一个前缀)。该grep
解决方案需要让所有的搜索表达式在内存中。
perl -ne 'print if ($seen{$_} .= @ARGV) =~ /10$/' file1 file2
comm
命令更好,因为它搜索file1
in的每一行,file2
其中comm
仅当line n
in file1
等于line n
in 时才进行比较file2
。
comm
不能简单地将file1中的N行与file2中的N行进行比较。它可以很好地管理在两个文件中插入的一系列行(当然,这等效于从另一个文件中删除一系列的行)。它仅要求输入按排序顺序。
comm
如果要保留订单,总比回答好。awk
如果不想重复,总比回答好。
如果这两个文件尚未排序,则可以使用:
comm -12 <(sort a.txt) <(sort b.txt)
它将起作用,避免comm: file 2 is not in sorted order
这样做时出现错误消息comm -12 a.txt b.txt
。
<(command)
,尽管进程替换语法可以在Bash和其他一些应用程序中使用,但它不能移植到POSIX Shell中。
在受限版本的Linux上(例如我正在开发的QNAP(nas)):
grep -f file1 file2
可能会导致某些问题,如@ChristopherSchultz所说,使用grep -F -f file1 file2
速度真的很慢(超过5分钟-未完成-超过2-3秒使用下面的方法处理超过20MB的文件)所以这就是我所做的:
sort file1 > file1.sorted
sort file2 > file2.sorted
diff file1.sorted file2.sorted | grep "<" | sed 's/^< *//' > files.diff
diff file1.sorted files.diff | grep "<" | sed 's/^< *//' > files.same.sorted
如果files.same.sorted
应该以与原始顺序相同的顺序,则以与file1相同的顺序添加此行:
awk 'FNR==NR {a[$0]=$0; next}; $0 in a {print a[$0]}' files.same.sorted file1 > files.same
或者,与file2相同的顺序:
awk 'FNR==NR {a[$0]=$0; next}; $0 in a {print a[$0]}' files.same.sorted file2 > files.same
仅供参考,如果有人仍在寻找如何对多个文件执行此操作,请参阅在多个文件中查找匹配行的链接答案。
结合这两个答案(ans1和ans2),我认为无需排序文件就可以得到所需的结果:
#!/bin/bash
ans="matching_lines"
for file1 in *
do
for file2 in *
do
if [ "$file1" != "$ans" ] && [ "$file2" != "$ans" ] && [ "$file1" != "$file2" ] ; then
echo "Comparing: $file1 $file2 ..." >> $ans
perl -ne 'print if ($seen{$_} .= @ARGV) =~ /10$/' $file1 $file2 >> $ans
fi
done
done
只需保存它,赋予它执行权限(chmod +x compareFiles.sh
)并运行它。它将获取当前工作目录中存在的所有文件,并进行全对所有比较,结果保留在“ matching_lines”文件中。
有待改进的地方:
rm file3.txt
cat file1.out | while read line1
do
cat file2.out | while read line2
do
if [[ $line1 == $line2 ]]; then
echo $line1 >>file3.out
fi
done
done
这应该做。
rm -f file3.txt
如果您要删除文件,则应该使用;如果文件不存在,则不会报告任何错误。OTOH,如果您的脚本只是简单地回显到标准输出,则无需用户让脚本用户选择输出应到达的位置。最终,您可能希望使用$1
和$2
(命令行参数)而不是固定文件名(file1.out
和file2.out
)。剩下的就是算法:它将变得很慢。它将file2.out
为中的每一行读取一次file1.out
。如果文件很大(例如多个千字节),它将很慢。
grep -F
这样的工具可以将一个文件读入内存,然后在另一个文件上进行一次传递,从而避免在两个输入文件上重复循环。
comm
需要排序的输入文件。如果您只想逐行通用,那就太好了。但是,如果您想要我所谓的“ anti-diff”,那就comm
做不到。