我有两个文件A
- nodes_to_delete
和B
- nodes_to_keep
。每个文件都有许多带有数字ID的行。
我希望获得nodes_to_delete
但不在nodes_to_keep
中的数字ID列表,例如alt text http://mathworld.wolfram.com/images/equations/SetDifference/Inline1.gif
在PostgreSQL数据库中执行此操作非常慢。使用Linux CLI工具在bash中做任何巧妙的方法吗?
更新:这似乎是一个Pythonic工作,但文件确实很大。我使用uniq
,sort
和一些集合理论技术解决了一些类似的问题。这比数据库等价物快两到三个数量级。
答案 0 :(得分:93)
comm命令可以做到这一点。
答案 1 :(得分:38)
有人在几个月前向我展示了如何做到这一点,然后我暂时找不到它......而且看着我偶然发现了你的问题。这是:
set_union () {
sort $1 $2 | uniq
}
set_difference () {
sort $1 $2 $2 | uniq -u
}
set_symmetric_difference() {
sort $1 $2 | uniq -u
}
答案 2 :(得分:4)
使用comm
- 它将逐行比较两个已排序的文件。
此命令将返回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。请注意三列。
$ 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
答案 3 :(得分:3)
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 :(得分:1)
也许你需要一个更好的方法在postgres中做到这一点,我可以打赌你不会找到一个更快的方法来使用平面文件。你应该能够做一个简单的内连接,并假设两个id cols都被索引,应该非常快。
答案 5 :(得分:0)
因此,这与其他答案略有不同。我不能说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 A
(由于A B
是B
,所以运行nodes_to_keep
(不是 mmap
),结果差异将被打印到stdout
请注意,为了让代码更简单,我已经放弃了一些C ++最佳实践。
可以进行许多其他速度优化(以增加内存为代价)。 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
):
const varArgs = f => {
const go = args =>
Object.defineProperties(
arg => go(args.concat(arg)), {
"runVarArgs": {get: function() {return f(args)}, enumerable: true},
[TYPE]: {value: "VarArgs", enumerable: true}
});
return go([]);
};
const varLiftM = (chain, of) => f => { // TODO: replace recursion with a fold
const go = (ms, g, i) =>
i === ms.length
? of(g)
: chain(ms[i]) (x => go(ms, g(x), i + 1));
return varArgs(ms => go(ms, f, 0));
};
答案 6 :(得分:0)
另一个可移植的解决方案,也适用于多重集,一个允许元素的多个实例的集合,是在单独的文件中使用 grep 和模式:
grep -Fvx -f B A
参数:
如果 B 中的模式与 A 中的一行不匹配,则该命令输出该行,否则不输出任何内容。
此解决方案的一个很好的功能是可以使其与多列文件一起使用(对于 A
),而 comm
和 uniq -u
解决方案需要一个列文件。< /p>