2つのファイルに共通する行を見つけるUnixコマンド


179

2つ以上のファイルから共通の行を出力する可能性のあるUNIXコマンドを見つけたことがあると思いますが、その名前を知っている人はいますか?それよりもはるかに簡単diffでした。


5
commソートされた入力ファイルが必要なため、この質問への回答は必ずしも誰もが望むものではありません。行ごとの共通のみが必要な場合、それは素晴らしいことです。しかし、もしあなたが私が "anti-diff"と呼ぶものを望むcommなら、その仕事はしません。
ロバートP.ゴールドマン

@ RobertP.Goldmanには、file1にのような部分的なパターンpr-123-xy-45が含まれ、file2にが含まれる場合に、2つのファイル間で共通にする方法がありますec11_orop_pr-123-xy-45.gz。file3が必要ですec11_orop_pr-123-xy-45.gz
Chandan Choudhury、2015年

テキストファイルを行
ごと

回答:


216

あなたが探しているコマンドはcommです。例えば:-

comm -12 1.sorted.txt 2.sorted.txt

ここに:

-1:列1を抑制(1.sorted.txtに固有の行)

-2:列2を抑制します(2.sorted.txtに固有の行)


27
典型的な使用法:comm -12 1.sorted.txt 2.sorted.txt
Fedir RYKHTIK

45
commはファイルをソートする必要がありますが、grep -f file1 file2を実行して、両方のファイルの共通行を取得できます。
ファーディ2015年

2
@ferdy(あなたの答えは本質的にコメントとして投稿された繰り返しの回答なので、あなたの答えから私のコメントを繰り返す)は、あなたが予期しgrepないかもしれない奇妙なことをします。具体的には、すべてが1.txt正規表現として解釈され、プレーンな文字列として解釈されません。また、の空白行はの1.txtすべての行と一致し2.txtます。したがってgrep、非常に特定の状況でのみ機能します。あなたは少なくとも使用したいと思うでしょうfgrep(またはgrep -f)が、空白行のことはおそらくこのプロセスに大混乱をもたらすでしょう。
クリストファーシュルツ

11
参照してくださいferdy答えは以下の、そしてクリストファー・シュルツさんとその上に私のコメントを。TL; DR —を使用しますgrep -F -x -f file1 file2
Jonathan Leffler、2015

1
@bapors:コマンドからの出力commを3つの個別のファイルに取得する方法として、自己回答型のQ&Aを提供しましたか? 答えは大きすぎて、ここに快適に収まりません。
Jonathan Leffler 2017

62

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」を含む1行が共通しています。ソートされていないファイルで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行を手に入れました!


2
そう... sort abc > abc.sortedsort dev > def.sortedその後、comm -12 abc.sorted def.sorted
Nikana Reklawyks 2017年

1
@NikanaReklawyksそして、後で一時ファイルを削除し、エラーが発生した場合のクリーンアップに対処することを忘れないでください。多くのシナリオでは、結果がメモリに収まる限り、ディスクI / Oを回避できるため、プロセスの置換も非常に高速になります。
tripleee 2017

29

Perlのワンライナーを補足するために、以下はawk同等のものです。

awk 'NR==FNR{arr[$0];next} $0 in arr' file1 file2

これfile1により、からすべての行が配列arr[]に読み込まれ、各行file2が配列内に既に存在するかどうか(つまり、file1)がチェックされます。見つかった行は、に表示される順序で印刷されfile2ます。比較でin arrは、file2インデックスから配列までの行全体が使用されるため、行全体の完全一致のみが報告されることに注意してください。


2
これが正解です。他のどれも一般的に動作させることはできません(私はそれらを試したことがないperlので)。おかげで百万、さん
entonio

1
共通の行を表示するときに順序を維持することは、そのためにcommを除外する場合に非常に役立ちます。
tuxayo 2016

1
誰かが特定の列に基づいて同じことをしたいがawkを知らない場合は、たとえば列5の両方の$ 0を$ 5に置き換えるだけで、列5に同じ単語がある2つのファイルで行を共有できます
FatihSarigol

24

もしかしてcomm

ソートされたファイルFILE1とFILE2を行ごとに比較します。

オプションなしで、3列の出力を生成します。1列目にはFILE1に固有の行が含まれ、2列目にはFILE2に固有の行が含まれ、3列目には両方のファイルに共通の行が含まれています。

これらの情報を見つける秘訣は、情報ページです。GNUプログラムの場合、それらはmanページよりもはるかに詳細です。試してみてinfo coreutilsください。小さな便利なユーティリティがすべて一覧表示されます。


19

ながら

grep -v -f 1.txt 2.txt > 3.txt

2つのファイルの違い(2.txtにあり、1.txtにないもの)を提供するため、簡単に

grep -f 1.txt 2.txt > 3.txt

すべての一般的な行を収集します。これにより、問題を簡単に解決できます。ファイルを並べ替えている場合でも、commそれを行う必要があります。よろしく!


2
grepあなたが期待しないかもしれないいくつかの奇妙なことをします。具体的には、すべてが1.txt正規表現として解釈され、プレーンな文字列として解釈されません。また、の空白行はの1.txtすべての行と一致し2.txtます。したがって、これは非常に特定の状況でのみ機能します。
クリストファーシュルツ

13
@ChristopherSchultz:最新のUnixバリアントのほとんどでgrepサポートされているPOSIX 表記を使用して、この回答をアップグレードして作業を改善することができますgrep。追加-F(またはを使用fgrep)して、正規表現を抑制します。-x行全体にのみ一致するように(正確に)追加します。
Jonathan Leffler、2015

なぜcommソートされたファイルをとるべきなのですか?
Ulysse BN

2
@UlysseBN commは、メモリに3行を保持するだけでよいので、並べ替えられている限り、任意の大きなファイルで機能します(comm行が本当に長い場合、GNU はプレフィックスだけを保持することさえ知っていると思います)。grep解決策は、メモリ内のすべての検索式を維持する必要があります。
tripleee 2017

9

2つのファイルがまだソートされていない場合は、以下を使用できます。

comm -12 <(sort a.txt) <(sort b.txt)

そしてそれは機能し、comm: file 2 is not in sorted order 実行時のエラーメッセージを回避しますcomm -12 a.txt b.txt


あなたは正しいですが、これは基本的に別の答えを繰り返していますが、実際には何のメリットもありません。十分に確立された正しい回答のある古い質問に回答することにした場合、その日の後半に新しい回答を追加しても、クレジットを得られない可能性があります。特徴的な新しい情報がある場合、または他の回答がすべて間違っていると確信している場合は、必ず新しい回答を追加してください。 tたくさんのクレジットを獲得してください。
Jonathan Leffler 2017

私はこの答え@JonathanLefflerを見さえしませんでした。この部分が答えの最後にあり、以前に他の答えの要素と混ざっていたからです。他の答えはより正確ですが、私の解決策の利点は、簡単な解決策を求めている人にとっては2行しか読めないことです。時々、私たちは詳細な答えを探していて、時々私たちは急いでいて、すぐに読める、すぐに貼り付けられる答えがいいです。
Basj

また、私はクレジット/担当者を気にしません、私はこの目的で投稿しませんでした。
Basj

1
また、プロセス置換構文<(command)はPOSIXシェルに移植できませんが、Bashやその他の一部では機能します。
tripleee 2017

8
perl -ne 'print if ($seen{$_} .= @ARGV) =~ /10$/'  file1 file2

これは、より良いよりも働いているcomm、それはそれぞれの行を検索すると、コマンドfile1file2どこcomm行場合にのみ比較されますnfile1、ISは、ラインに等しいですnfile2
teriiehina 2014年

1
@teriiehina:いいえ。commfile1の行Nをfile2の行Nと比較するだけではありません。どちらかのファイルに挿入された一連の行を完全にうまく管理できます(もちろん、他のファイルから一連の行を削除することと同じです)。入力がソートされている必要があります。
Jonathan Leffler、2015

comm順序を維持したい場合は、回答よりも優れています。awk重複を望まない場合は、答えるよりはましです。
tuxayo 2016



3

Linuxの限定バージョン(私が取り組んでいたQNAP(nas)など):

  • 通信は存在しませんでした
  • grep -f file1 file2@ChristopherSchultzで述べられているようにいくつかの問題を引き起こす可能性があり、使用grep -F -f file1 file2が非常に遅かった(5分以上-終了しなかった-20MBを超えるファイルで以下の方法で2〜3秒以上)

だからここに私がやったことがあります:

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

2

参考までに、複数のファイルに対してこれを行う方法をまだ誰かが探している場合は、多くのファイルで一致する行を見つけることへのリンクされた回答を参照してください


これらの2つの答え(ans1ans2)を組み合わせると、ファイルを並べ替えなくても必要な結果を得ることができると思います。

#!/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)を与えて実行するだけです。現在の作業ディレクトリに存在するすべてのファイルを取得し、all-vs-all比較を実行して、「matching_lines」ファイルに結果を残します。

改善すべき点:

  • ディレクトリをスキップ
  • すべてのファイルを2回比較しないでください(file1とfile2およびfile2とfile1)。
  • 多分一致する文字列の横に行番号を追加します

-2
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

これでうまくいくはずです。


1
rm -f file3.txtファイルを削除する場合は、おそらく使用する必要があります。ファイルが存在しない場合、エラーは報告されません。OTOH、スクリプトが標準出力にエコーするだけの場合、スクリプトのユーザーが出力先を選択できるようにする必要はありません。最終的には、固定ファイル名(および)の代わりに$1and $2(コマンドライン引数)を使用したいと思うでしょう。それはアルゴリズムを残します:それは遅くなるでしょう。の各行に1回読み込まれます。ファイルが大きい場合(たとえば、数キロバイト)は遅くなります。file1.outfile2.outfile2.outfile1.out
Jonathan Leffler、2015

シェルのメタ文字を含まない入力がある場合、これは名目上は機能しますが(ヒント:shellcheck.netからどのような警告が表示されるかを確認してください)、この単純なアプローチは非常に非効率的です。grep -F1つのファイルをメモリに読み込んでから、もう1つのパスを1回実行するようなツールは、両方の入力ファイルで繰り返しループすることを回避します。
tripleee 2017
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.