web-dev-qa-db-ja.com

bash、Linux:2つのテキストファイルの違いを設定

2つのファイルA-nodes_to_deleteおよびB-nodes_to_keepがあります。各ファイルには、数値IDを持つ多数の行があります。

nodes_to_deleteにはあるがnodes_to_keepにはない数値IDのリストが必要です。 alt text

PostgreSQLデータベース内でそれを行うことは、不当に遅いです。 Linux CLIツールを使用してbashでそれを行うための素敵な方法はありますか?

UPDATE:これはPythonicの仕事のように思えますが、ファイルは本当に、本当に大きいです。 uniqsort、およびいくつかの集合理論手法を使用して、いくつかの同様の問題を解決しました。これは、同等のデータベースよりも約2〜3桁高速でした。

60
Adam Matan

comm コマンドはそれを行います。

97
msw

誰かが私に数ヶ月前にshでこれを正確に行う方法を教えてくれたのですが、しばらくそれを見つけることができませんでした...そして見ながらあなたの質問につまずきました。ここにあります :

set_union () {
   sort $1 $2 | uniq
}

set_difference () {
   sort $1 $2 $2 | uniq -u
}

set_symmetric_difference() {
   sort $1 $2 | uniq -u
}
38
slinkp

commを使用-ソートされた2つのファイルを行ごとに比較します。

あなたの質問への短い答え

このコマンドは、deleteNodesに固有の行を返しますが、keepNodesの行は返しません。

comm -1 -3 <(sort keepNodes) <(sort deleteNodes)

設定例

keepNodesおよびdeleteNodesという名前のファイルを作成し、commコマンドのソートされていない入力として使用してみましょう。

$ cat > keepNodes <(echo bob; echo amber;)
$ cat > deleteNodes <(echo bob; echo ann;)

デフォルトでは、引数なしでcommを実行すると、次のレイアウトで3列が印刷されます。

lines_unique_to_FILE1
    lines_unique_to_FILE2
        lines_which_appear_in_both

上記のサンプルファイルを使用して、引数なしでcommを実行します。 3つの列に注意してください。

$ comm <(sort keepNodes) <(sort deleteNodes)
amber
    ann
        bob

列出力の抑制

-Nで列1、2、または3を抑制します。列が非表示になると、空白が縮小することに注意してください。

$ comm -1 <(sort keepNodes) <(sort deleteNodes)
ann
    bob
$ comm -2 <(sort keepNodes) <(sort deleteNodes)
amber
    bob
$ comm -3 <(sort keepNodes) <(sort deleteNodes)
amber
    ann
$ comm -1 -3 <(sort keepNodes) <(sort deleteNodes)
ann
$ comm -2 -3 <(sort keepNodes) <(sort deleteNodes)
amber
$ comm -1 -2 <(sort keepNodes) <(sort deleteNodes)
bob

ソートは重要です!

最初にファイルをソートせずにcommを実行すると、正常に失敗し、どのファイルがソートされていないかに関するメッセージが表示されます。

comm: file 1 is not in sorted order

6
activedecay

commは、この種のユースケース用に特別に設計されましたが、ソートされた入力が必要です。

awkは、セットの違いを見つけるのがかなり簡単であり、sortを必要とせず、柔軟性が向上するため、おそらくこれに適したツールです。

awk 'NR == FNR { a[$0]; next } !($0 in a)' nodes_to_keep nodes_to_delete

おそらく、たとえば、負でない数を表す行の違いだけを見つけたいと思うでしょう:

awk -v r='^[0-9]+$' 'NR == FNR && $0 ~ r {
    a[$0]
    next
} $0 ~ r && !($0 in a)' nodes_to_keep nodes_to_delete
4
John B

Postgresでもっと良い方法が必要なのかもしれませんが、フラットファイルを使用してより高速な方法を見つけることはできないでしょう。単純な内部結合を実行でき、両方のid colにインデックスが付けられていると仮定すると、非常に高速になります。

1
Dark Castle

したがって、これは他の回答とは少し異なります。 C++コンパイラがまさに「Linux CLIツール」であるとは言えませんが、g++ -O3 -march=native -o set_diff main.cppを実行しています(main.cppに以下のコードを使用すると、トリックを実行できます)。

#include<algorithm>
#include<iostream>
#include<iterator>
#include<fstream>
#include<string>
#include<unordered_set>

using namespace std;

int main(int argc, char** argv) {
    ifstream keep_file(argv[1]), del_file(argv[2]);
    unordered_multiset<string> init_lines{istream_iterator<string>(keep_file), istream_iterator<string>()};
    string line;
    while (getline(del_file, line)) {
        init_lines.erase(line);
    }
    copy(init_lines.begin(),init_lines.end(), ostream_iterator<string>(cout, "\n"));
}

使用するには、set_diff B AnotA BBnodes_to_keepであるため)を実行するだけで、結果の差が標準出力に出力されます。

コードをシンプルにするために、C++のいくつかのベストプラクティスを無視していることに注意してください。

多くの追加の速度最適化を行うことができます(より多くのメモリを犠牲にして)。 mmapは、大きなデータセットの場合にも特に役立ちますが、それによりコードがより複雑になります。

データセットが大きいと述べたので、メモリ消費を削減するには、一度に1行ずつnodes_to_deleteを読み取ることをお勧めします。 nodes_to_deleteに大量の重複がある場合、上記のコードで採用されているアプローチは特に効率的ではありません。また、順序は保持されません。


簡単にコピーしてbashに貼り付ける(つまり、main.cppの作成をスキップする):

g++ -O3 -march=native -xc++ -o set_diff - <<EOF
#include<algorithm>
#include<iostream>
#include<iterator>
#include<fstream>
#include<string>
#include<unordered_set>

using namespace std;

int main(int argc, char** argv) {
        ifstream keep_file(argv[1]), del_file(argv[2]);
        unordered_multiset<string> init_lines{istream_iterator<string>(keep_file), istream_iterator<string>()};
        string line;
        while (getline(del_file, line)) {
                init_lines.erase(line);
        }
        copy(init_lines.begin(),init_lines.end(), ostream_iterator<string>(cout, "\n"));
}
EOF
0
YenForYang