基于一列查找文件中的公共行

时间:2012-05-21 00:52:48

标签: shell unix awk

我有15个文件,比如

  

file1.csv

a,cg2,0,0,0,21,0
a,cq1,10,0,0,0,0
a,cm2,0,19,0,0,0
...
a,ad10,0,0,0,37,0
  

file2.csv

d,cm1,0,3,0,0,0
d,cs2,0,32,0,0,0
d,cg2,0,0,9,0,0
...
d,az2,0,0,0,21,0

。 。 。

  

file15.csv

s,sd1,0,23,0,0,0
s,cw1,0,0,7,0,0
s,c23,0,0,90,0,0
...
s,cg2,0,45,0,0,0

我在每个文件中有不同的行数,我想比较所有15个文件的第二个字段,并提取所有15个文件的第二个字段共有的行。

在上述情况

输出是:

CG2

(所有15个文件的第二个字段都是常见的)

我不熟悉unix和shell脚本,请帮忙

2 个答案:

答案 0 :(得分:3)

你想要15个文件中的每个文件的全部行,其中字段2出现在所有15个文件中吗?或者您只想要一个列在所有十五个文件中的字段2值的列表。

前者:

a,cg2,0,0,0,21,0
d,cg2,0,0,9,0,0
. . .
s,cg2,0,45,0,0,0
. . .

后者:

cg2
. . .

如果是后者,那么这应该有效

awk -F, '{arr[$2]++; if (FILENAME != prevfile) {c++; prevfile = FILENAME}} END {for (i in arr) {if (arr[i] == c) {print i}}}' file*.csv

分为多行:

awk -F, '{
             arr[$2]++; 
             if (FILENAME != prevfile) {
                 c++; 
                 prevfile = FILENAME
             }
         }
         END {
             for (i in arr) {
                 if (arr[i] >= c) {
                     print i
                 }
             }
         }' file*.csv

说明:

  • 增加字段2值出现次数的计数
  • 如果文件名发生变化,则增加文件数(第一个文件从空字符串变为文件名,计数从0增加到1)
  • 保存当前文件名
  • 完成所有计数后,通过其键重复数组
  • 如果数组中包含的计数大于或等于文件数,则字段2值出现在所有文件中(通过检查>=代替==这将起作用如果值在单个文件中出现多次)
  • 所以打印键(字段2值)
  • glob用于获取所有文件,但您可以明确列出它们

修改

这是一种使用双程技术打印完整匹配线的方法。这是对上述版本的修改。确保两次列出文件。

awk -F, '
         FILENAME == first && flag {
             exit
         }
         ! first {
             first = FILENAME
         }
         FILENAME != first {
             flag = 1
         }
         {
             arr[$2]++; 
             if (FILENAME != prevfile) {
                 c++; 
                 prevfile = FILENAME
             }
         }
         END {
             # print the matching lines
             do {
                 if ($2 in arr) {
                     print;
                 }
             } while (getline);
             # print the list of words
             for (i in arr) {
                 if (arr[i] >= c) {
                     print i
                 }
             }
         }' file*.csv file*.csv

它取决于第一个组中的第一个文件与第二个组中的第一个文件同名。使用类似于我所展示的globbing将处理该要求。

它打印匹配的行(不分组),然后打印单词列表。如果您只想要其中一个,请注释掉或删除您不想要的循环(do/whilefor)。

如果只打印整行,可以将输出通道输出到:

sort -t , -k2,2

让他们分组。

仅将单词列表用于:

sort

将它们放在相同的顺序中以便于比较。

答案 1 :(得分:1)

有趣的问题。

完全用Bash完成的一种方法如下。

您需要调用的一件事是join -t ',' -1 2 -2 2 file1 file2加入两个文件的第二列。但是,在加入之前,您必须对第二列进行排序。

在for循环中进行连续连接,因为join只接受两个文件作为参数。

附录

这是一个显示连续加入的小记录。我认为你可以很容易地调整它。

$ cat 1.csv
a,b,c,d
e,f,g,h
i,j,k,l
$ cat 2.csv
7,5,4,3
3,b,s,e
2,f,5,5
$ cat 3.csv
4,5,6,7
0,0,0,0
1,b,4,4
$ join -t ',' -1 2 -2 2 1.csv 2.csv | cut -f 1 -d ',' > temp
$ cat temp
b
f
$ join -t ',' -2 2 temp 3.csv | cut -f 1 -d ','
b

第一个连接(在前两个文件上)在结果的第一列中生成连接值。因此,当您加入file3,file4,file5等时,您将使用您生成的结果的第一列,这就是您只需要-2选项的原因。为了保持高效,每次进行连接时,请始终删除除第一列之外的所有列。