7

我有三个文件,如下所示

文件1.txt

"aba" 0 0 
"aba" 0 0 1
"abc" 0 1
"abd" 1 1 
"xxx" 0 0

文件2.txt

"xyz" 0 0
"aba" 0 0 0 0
"aba" 0 0 0 1
"xxx" 0 0
"abc" 1 1

文件 3.txt

"xyx" 0 0
"aba" 0 0 
"aba" 0 1 0
"xxx" 0 0 0 1
"abc" 1 1

我想根据前两列在所有三个文件中找到相似的元素。为了在两个文件中找到相似的元素,我使用了类似的东西

awk 'FNR==NR{a[$1,$2]++;next}a[$1,$2]' file1.txt file2.txt 

但是,当输入文件超过 2 个时,我们如何在所有文件中找到相似的元素呢?任何人都可以帮忙吗?

使用当前的 awk 解决方案,输出会忽略重复的键列,并将输出作为

"xxx" 0 0

如果我们假设输出来自 file1.txt,那么预期的输出是:

"aba" 0 0 
"aba" 0 0 1
"xxx" 0 0 

即它也应该得到具有重复键列的行。

4

3 回答 3

3

尝试以下针对N 个文件通用的解决方案。它将第一个文件的数据保存在值为 的哈希中1,并且对于来自下一个文件的每次命中,该值都会增加。最后,我比较每个键的值是否与处理的文件数相同,并仅打印匹配的文件。

awk '
    FNR == NR { arr[$1,$2] = 1; next }
    { if ( arr[$1,$2] ) { arr[$1,$2]++ } }
    END { 
        for ( key in arr ) {
            if ( arr[key] != ARGC - 1 ) { continue }
            split( key, key_arr, SUBSEP )
            printf "%s %s\n", key_arr[1], key_arr[2] 
        } 
    }
' file{1..3}

它产生:

"xxx" 0
"aba" 0

编辑以添加打印整行的版本(见评论)。我在保存行的位置添加了另一个具有相同键的数组,并在printf函数中使用它。我已将旧代码注释掉。

awk '
    ##FNR == NR { arr[$1,$2] = 1; next }
    FNR == NR { arr[$1,$2] = 1; line[$1,$2] = $0; next }
    { if ( arr[$1,$2] ) { arr[$1,$2]++ } }
    END { 
        for ( key in arr ) {
            if ( arr[key] != ARGC - 1 ) { continue }
            ##split( key, key_arr, SUBSEP )
            ##printf "%s %s\n", key_arr[1], key_arr[2] 
            printf "%s\n", line[ key ] 
        } 
    }
' file{1..3}

新编辑(见评论)添加一个版本,用相同的键处理多行。基本上我加入了所有条目,而不是只保存一个,line[$1,$2] = $0line[$1,$2] = line[$1,$2] ( line[$1,$2] ? SUBSEP : "" ) $0. 在打印时,我使用分隔符(SUBSEP变量)进行反向拆分并打印每个条目。

awk '
    FNR == NR { 
        arr[$1,$2] = 1
        line[$1,$2] = line[$1,$2] ( line[$1,$2] ? SUBSEP : "" ) $0
        next
    }
    FNR == 1 { delete found }
    { if ( arr[$1,$2] && ! found[$1,$2] ) { arr[$1,$2]++; found[$1,$2] = 1 } }
    END { 
        num_files = ARGC -1 
        for ( key in arr ) {
            if ( arr[key] < num_files ) { continue }
            split( line[ key ], line_arr, SUBSEP )
            for ( i = 1; i <= length( line_arr ); i++ ) { 
                printf "%s\n", line_arr[ i ]
            } 
        } 
    }
' file{1..3}

使用有问题的新数据编辑,它产生:

"xxx" 0 0
"aba" 0 0 
"aba" 0 0 1
于 2013-06-05T09:52:19.843 回答
1

对于三个文件,您只需要:

awk 'FNR==NR { a[$1,$2]; next} ($1,$2) in a' file1.txt file2.txt file3.txt

FNR==NR块仅对参数列表中的第一个文件返回 true。此块中的next语句强制跳过剩余的代码。因此,($1,$2) in a对参数列表中除第一个文件之外的所有文件执行。要以您拥有的方式处理更多文件,您需要做的就是列出它们。


如果您需要在命令行上使用更强大的 globbing,请使用extglob. 您可以使用 将其打开shopt -s extglob,并使用 将其关闭shopt -u extglob。例如:

awk 'FNR==NR { a[$1,$2]; next} ($1,$2) in a' file1.txt !(file1.txt)

如果您很难找到文件,请使用find. 例如:

awk 'FNR==NR { a[$1,$2]; next} ($1,$2) in a' file1.txt $(find /path/to/files -type f -name "*[23].txt")

我假设您正在寻找“N”个文件的全局范围。例如:

awk 'FNR==NR { a[$1,$2]; next} ($1,$2) in a' file1.txt file{2,3}.txt
于 2013-06-05T11:12:26.200 回答
1

这个 python 脚本将列出所有文件中的公共行:

import sys
i,l = 0,[]
for files in sys.argv[1:]:
  l.append(set())
  for line in open(files): l[i].add(" ".join(line.split()[0:2]))
  i+=1
commonFields =  reduce(lambda s1, s2: s1 & s2, l)
for files in sys.argv[1:]:
  print "Common lines in ",files
  for line in open(files):
    for fields in commonFields:
      if fields in line:
        print line,
        break

用法:python script.py file1 file2 file3 ...

于 2013-06-05T10:11:19.080 回答