bash,Linux:设置两个文本文件之间的差异

bash,Linux:设置两个文本文件之间的差异,bash,file-io,set-difference,Bash,File Io,Set Difference,我有两个文件A-节点要删除和B-节点要保留。每个文件都有许多带有数字ID的行 我想要在节点中删除但不在节点中保留的数字ID列表,例如。 在PostgreSQL数据库中执行此操作的速度慢得不合理。在bash中使用linuxcli工具有什么好方法可以做到这一点吗 更新:这看起来像是一个Pythonic作业,但是文件非常非常大。我用uniq,sort和一些集合论技术解决了一些类似的问题。这比数据库的等价物快了大约两到三个数量级。这个命令可以做到这一点。也许在postgres中需要更好的方法,我敢打赌

我有两个文件
A
-
节点要删除
B
-
节点要保留
。每个文件都有许多带有数字ID的行

我想要在
节点中删除但不在
节点中保留的数字ID列表,例如。

在PostgreSQL数据库中执行此操作的速度慢得不合理。在bash中使用linuxcli工具有什么好方法可以做到这一点吗


更新:这看起来像是一个Pythonic作业,但是文件非常非常大。我用
uniq
sort
和一些集合论技术解决了一些类似的问题。这比数据库的等价物快了大约两到三个数量级。

这个命令可以做到这一点。

也许在postgres中需要更好的方法,我敢打赌,使用平面文件不会找到更快的方法。你应该能够做一个简单的内部连接,并假设两个id列都被索引,速度应该非常快。

几个月前,有人向我展示了如何在sh中完全做到这一点,然后我一时找不到它。。。在看的时候,我偶然发现了你的问题。这是:

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

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

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

使用
comm
-它将逐行比较两个已排序的文件

对你问题的简短回答 此命令将返回deleteNodes特有的行,而不是keepNodes中的行

comm -1 -3 <(sort keepNodes) <(sort deleteNodes)
默认情况下,在不带参数的情况下运行comm会打印具有此布局的3列:

lines_unique_to_FILE1
    lines_unique_to_FILE2
        lines_which_appear_in_both
使用上面的示例文件,不带参数地运行comm。请注意这三列

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

$comm
comm
是专门为这种用例设计的,但它需要排序输入

awk
可以说是一个更好的工具,因为它可以非常直接地找到集合差异,不需要
排序
,并提供额外的灵活性

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

因此,这与其他答案略有不同。我不能说C++编译器是一个“Linux CLI工具”,但是运行<代码> G++-O3-行进= NeX-OSETHOSTIFION CAMP.CPP (下面的代码在<代码> Meal.CPP < /代码>可以做到这一点):

#包括
#包括
#包括
#包括
#包括
#包括
使用名称空间std;
int main(int argc,字符**argv){
ifstream keep_文件(argv[1])、del_文件(argv[2]);
无序的多集初始化行{istream_迭代器(keep_文件),istream_迭代器();
弦线;
while(getline(del_文件,第行)){
初始化行。擦除(行);
}
复制(init_lines.begin()、init_lines.end()、ostream_迭代器(cout,“\n”));
}
要使用,只需运行
set_diff B A
(而不是
A B
,因为
B
nodes\u To _keep
),结果差异将打印到标准输出

注意,我放弃了一些C++最佳实践来保持代码更简单。 可以进行许多额外的速度优化(以牺牲更多内存为代价)

mmap
对于大型数据集也特别有用,但这会使代码更加复杂

由于您提到数据集很大,我认为一次读取
节点以删除
一行可能是减少内存消耗的一个好主意。如果
节点中存在大量重复项,则上述代码中采用的方法不是特别有效。此外,秩序也得不到维护


更容易复制和粘贴到
bash
(即跳过创建
main.cpp
):


g++-O3-march=native-xc++-o set_diff-另一个可移植的解决方案,也适用于Multiset(允许元素的多个实例的集合)的情况,即在单独的文件中使用grep和模式:

grep -Fvx -f B A
参数:

  • -f:包含模式列表的文件,一行一行
  • -F:将模式视为字符串,而不是正则表达式
  • -x:将A节点中的整行匹配到删除
  • -v:反转匹配(如果不匹配则匹配)
如果B中的图案与a中的某一行不匹配,则该命令将输出该行,否则将不输出任何内容


这个解决方案的一个很好的特点是,它可以处理多列文件(对于
A
),而
comm
uniq-u
解决方案需要一列文件。

我很好奇会得到什么答案。Bash更像是Segfault,我相信是系统管理员。如果你说“用python”或“用php”或其他什么,你的机会会更好:)我看到了标题,准备抨击UI不一致和比你更神圣的帮助论坛。这让我在阅读实际问题时感到失望(如果文件还没有排序,
sort
first.+1开明的、很棒的工具,我觉得自己不知道。谢谢!@只是不会在这里挑起一场火焰战,但你的评论太粗鲁了。@Adam:讽刺的是,那个“comm”有点神秘可以追溯到一个时代,在所有这些花哨的Perl、pythons和MySQL出现之前,你可以将/bin和/usr/bin的全部内容保存在脑海中。回到那些简单的V7时代,你必须使用所有的工具,或者(喘息!)用ed(1)在雪地里写你自己的,两种方式都是上坡的,我们都喜欢!)如果我晚一点开始的话,我可能永远不会知道通信。@Adam Matan:对不起,粗鲁绝对不是我的本意。事实上,我发布的命令是一个很好的方式来了解这个系统,我曾经做过类似的事情来启发自己。否则,e。g
join(1)
对我来说是未知的。你在技术上是正确的,
explain
支持你的说法,但它根本不适用于非常大的(数千万)表。是的,它会受到内存的限制,不像排序的comm,但我认为如果你有两个表,只有一个int-id字段,你可以进入10秒的mi
awk -v r='^[0-9]+$' 'NR == FNR && $0 ~ r {
    a[$0]
    next
} $0 ~ r && !($0 in a)' nodes_to_keep nodes_to_delete
grep -Fvx -f B A