1

我有 15 个文件,例如

文件 1.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

文件2.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

. . . .

文件 15.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脚本有点陌生,请帮忙

4

2 回答 2

3

您是否想要字段 2 出现在所有 15 个文件中的 15 个文件中的每个文件的完整行?或者您是否只需要出现在所有 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

它取决于第一组中的第一个文件与第二组中的第一个文件同名。使用与我所展示的类似的通配符将满足该要求。

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

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

sort -t , -k2,2

让他们分组。

仅将单词列表传递到:

sort

将它们按相同的顺序排列以便于比较。

于 2012-05-21T02:10:11.677 回答
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 选项的原因。为了使事情保持高效,每次进行联接时,请始终删除除第一列之外的所有内容。

于 2012-05-21T01:02:43.547 回答