Linuxターミナルで2つのファイルを比較する


168

「a.txt」「b.txt」という2つのファイルがあり、どちらにも単語のリストがあります。今、私は中に余分である言葉をチェックしたい「A.TXT」としていない「B.TXT」

2つの辞書を比較する必要があるため、効率的なアルゴリズムが必要です。


27
diff a.txt b.txt十分ではありません?
ThanksForAllTheFish 2013年

各ファイルで単語が複数回出現する可能性はありますか?ファイルを並べ替えることができますか?
Basile Starynkevitch 2013年

「b.txt」になく、a.txtにある単語だけが必要
Ali Imran

回答:


343

vimがインストールされている場合は、これを試してください。

vimdiff file1 file2

または

vim -d file1 file2

あなたはそれが素晴らしいでしょう。ここに画像の説明を入力してください


9
間違いなく素晴らしく、デザインが良く、違いを見つけやすい。オーマイゴッド
Zen

1
あなたの答えは素晴らしいですが、私の教師は私にライブラリ関数を使用しないように要求しました:P
Ali Imran

1
なんて素晴らしいツールでしょう!これは非常に役に立ちます。
user1205577 2015

1
それらの色の意味は何ですか?
zygimantus

1
色付きのコードは、2つのファイルで異なることを意味します。@zygimantus
Li

73

それらを並べ替えて使用しますcomm

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

comm(ソートされた)入力ファイルを比較し、デフォルトで3つの列を出力します。aに固有の行、bに固有の行、および両方に存在する行です。指定することにより-1-2および/または-3、あなたは、対応する出力を抑制することができます。したがってcomm -23 a b、aに固有のエントリーのみをリストします。私は<(...)構文を使用してファイルをオンザフライでソートします。すでにソートされている場合、これは必要ありません。


grepコマンドのみを使用して自分の回答を追加しました。それがより効率的かどうか教えてください
Ali Imran 2013年

3
@AliImranは、commファイル全体をメモリに保存せずに1回の実行でジョブを実行するため、より効率的です。ほとんどの場合、既にソートされている辞書を使用しているため、sortそれらを使用する必要さえありません。grep -f file1 file2一方、を使用すると、全体file1がメモリにロードされ、各行file2がそれらのすべてのエントリと比較されますが、効率ははるかに低くなります。これは主に、ソートされていない小規模の場合に役立ち-f file1ます。
Anders Johansson

1
「comm」コマンドを共有してくれた@AndersJohanssonに感謝します。確かにその気の利いたものです。私は頻繁にファイル間の外部結合を行わなければならないので、これでうまくいきます。
blispr 2017

改行文字に注意してください... \n比較するために含まれることもわかりました。
Bin


28

diffLinuxのツールを使用して2つのファイルを比較できます。あなたは使用することができます--changedグループ形式をして--unchangedグループ形式のフィルタに必要なデータにオプションを。

以下の3つのオプションを使用して、各オプションに関連するグループを選択できます。

  • '%<'はFILE1から行を取得します

  • '%>'はFILE2から行を取得します

  • ''(空の文字列)両方のファイルから行を削除します。

例:diff --changed-group-format = "%<" --unchanged-group-format = "" file1.txt file2.txt

[root@vmoracle11 tmp]# cat file1.txt 
test one
test two
test three
test four
test eight
[root@vmoracle11 tmp]# cat file2.txt 
test one
test three
test nine
[root@vmoracle11 tmp]# diff --changed-group-format='%<' --unchanged-group-format='' file1.txt file2.txt 
test two
test four
test eight

27

からの差分出力スタイルgit diffが必要な場合は、それを--no-indexフラグと一緒に使用して、gitリポジトリにないファイルを比較できます。

git diff --no-index a.txt b.txt

それぞれに約200kのファイル名文字列を含むいくつかのファイルを使用して、私は(組み込みtimeコマンドで)このアプローチと他のいくつかの回答のベンチマークを比較しました:

git diff --no-index a.txt b.txt
# ~1.2s

comm -23 <(sort a.txt) <(sort b.txt)
# ~0.2s

diff a.txt b.txt
# ~2.6s

sdiff a.txt b.txt
# ~2.7s

vimdiff a.txt b.txt
# ~3.2s

commはるかに高速であるように見えますが、git diff --no-indexdiffスタイルの出力に対する最速のアプローチのようです。


2018年3月25日更新--no-index gitリポジトリ内にいて、そのリポジトリ内の追跡されていないファイルを比較する場合を除き、実際にはフラグを省略できます。マニュアルページから:

このフォームは、ファイルシステム上の指定された2つのパスを比較するためのものです。Gitによって制御されている作業ツリーでコマンドを実行し、少なくとも1つのパスが作業ツリーの外を指している場合、またはGitによって制御されている作業ツリーの外でコマンドを実行する場合は、-no-indexオプションを省略できます。




4

使用comm -13 (ソートされたファイルが必要)

$ cat file1
one
two
three

$ cat file2
one
two
three
four

$ comm -13 <(sort file1) <(sort file2)
four

1

これが私の解決策です:

mkdir temp
mkdir results
cp /usr/share/dict/american-english ~/temp/american-english-dictionary
cp /usr/share/dict/british-english ~/temp/british-english-dictionary
cat ~/temp/american-english-dictionary | wc -l > ~/results/count-american-english-dictionary
cat ~/temp/british-english-dictionary | wc -l > ~/results/count-british-english-dictionary
grep -Fxf ~/temp/american-english-dictionary ~/temp/british-english-dictionary > ~/results/common-english
grep -Fxvf ~/results/common-english ~/temp/american-english-dictionary > ~/results/unique-american-english
grep -Fxvf ~/results/common-english ~/temp/british-english-dictionary > ~/results/unique-british-english

2
他の解決策を試しましたか?これらの解決策の1つは役に立ちましたか?あなたの質問は、多くのユーザーを引き付けるのに十分一般的ですが、あなたの答えは私の好みにより具体的です...私の特定のケースでsdiff -s file1 file2は役に立ちました。
メタファニエル2015

@Metafaniel私のソリューションはsdiffコマンドを使用しません。問題を解決するためにLinux組み込みコマンドのみを使用します。
Ali Imran 2015

-1

そのためにawkを使用します。テストファイル:

$ cat a.txt
one
two
three
four
four
$ cat b.txt
three
two
one

awk:

$ awk '
NR==FNR {                    # process b.txt  or the first file
    seen[$0]                 # hash words to hash seen
    next                     # next word in b.txt
}                            # process a.txt  or all files after the first
!($0 in seen)' b.txt a.txt   # if word is not hashed to seen, output it

重複が出力されます:

four
four

重複を避けるために、a.txtで新しく一致した各単語をseenハッシュに追加します。

$ awk '
NR==FNR {
    seen[$0]
    next
}
!($0 in seen) {              # if word is not hashed to seen
    seen[$0]                 # hash unseen a.txt words to seen to avoid duplicates 
    print                    # and output it
}' b.txt a.txt

出力:

four

単語リストが次のようにカンマで区切られている場合:

$ cat a.txt
four,four,three,three,two,one
five,six
$ cat b.txt
one,two,three

あなたはいくつかの余分なラップ(forループ)をしなければなりません:

awk -F, '                    # comma-separated input
NR==FNR {
    for(i=1;i<=NF;i++)       # loop all comma-separated fields
        seen[$i]
    next
}
{
    for(i=1;i<=NF;i++)
        if(!($i in seen)) {
             seen[$i]        # this time we buffer output (below):
             buffer=buffer (buffer==""?"":",") $i
        }
    if(buffer!="") {         # output unempty buffers after each record in a.txt
        print buffer
        buffer=""
    }
}' b.txt a.txt

今回の出力:

four
five,six
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.