7

以下に示すように、3つのファイルがあります

file1.txt

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

file2.txt

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

file3.txt

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

最初の 2 つの列に基づいて、3 つのファイルすべてで類似の要素を見つけたいと考えています。2つのファイルで同様の要素を見つけるために、次のようなものを使用しました

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}

NEW EDIT (コメントを参照) を使用して、同じキーで複数の行を処理するバージョンを追加します。基本的に、1 つだけを保存する代わりにすべてのエントリを結合し、 で変更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

3 つのファイルの場合、必要なのは次のとおりです。

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最初のファイルを除く、引数リスト内のすべてのファイルに対して実行されます。あなたが持っている方法でより多くのファイルを処理するには、それらをリストするだけです。


コマンドラインでより強力なグロビングが必要な場合は、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 に答える