如果其他行以当前行开头则删除行(按列)

如果其他行以当前行开头则删除行(按列)

我有一个文件 input.txt 如下标签分隔格式:

aaaa    bbbb
aaaa    bbbb    c
aaaa    bbbb    c   dd
aaaa    bbbb    cc
aaaa    bbbb    x
aaaa    bbbb    xx
dddd    eeee
dddd    eeee    f
dddd    eeee    f   g
dddd    eeee    fe
h   ii  j

对于每一行,检查是否有另一行已包含前导列。如果是这样,请删除该行。不然就留着吧让我们看一下这个例子。

  • 第一行被删除,因为还有另一行带有附加列,其第一列相同:第二行。在这种情况下,删除第一行并保留第二行。
  • 第二行被删除,因为还有另一行带有附加列,其第一列相同:第三行。在这种情况下,删除第二行并保留第三行。
  • 第三行未被删除,因为没有其他行的第一列相同。在这种情况下,保留第三行。

等等等等。输出文件应该是:

aaaa    bbbb    c   dd
aaaa    bbbb    cc
aaaa    bbbb    x
aaaa    bbbb    xx
dddd    eeee    f   g
dddd    eeee    fe
h   ii  j

也许我们可以找到一个可以平滑运行数百万行的解决方案。

答案1

这只是对输入进行反向排序,因此“foobar”位于“foo”之前,如果当前行(foo)是从每个字符的第一个字符开始的前一行(foobar)的子字符串,则不会打印当前行(foo)。

$ sort -r file | awk 'index(prev FS,$0 FS) != 1; {prev=$0}'
h   ii  j
dddd    eeee    fe
dddd    eeee    f   g
aaaa    bbbb    xx
aaaa    bbbb    x
aaaa    bbbb    cc
aaaa    bbbb    c   dd

如果输出顺序对您很重要,有多种方法可以解决这个问题,例如:

$ cat -n file | sort -k2r |
    awk '{orig=$0; $1=""} index(prev FS,$0 FS) != 1{print orig} {prev=$0}' |
    sort -n | cut -f2-
aaaa    bbbb    c   dd
aaaa    bbbb    cc
aaaa    bbbb    x
aaaa    bbbb    xx
dddd    eeee    f   g
dddd    eeee    fe
h   ii  j

答案2

您想要根据列(字段)而不是字符删除作为另一行前缀的任何行。这可以通过 awk(1) 实现。您首先要对数据进行反向排序,以便较长的行排在前面,因此如果一行是前缀,则它位于它作为前缀的行之后。然后您可以使用 awk 扫描字段以查看它们是否与您保存的最后一行匹配,如果是,则删除它:

sort -r input.txt | awk '
    { for (i=1; i<=NF; i++) if (save[i] != $i) {keep=1; break} }
    keep == 0 { next }
    { delete save; for (i=1; i<=NF; i++) save[i]=$i; keep=0; print }
'

第一个 awk 操作将当前字段与保存的字段集进行比较。如果任何字段不同,我们会将这条线标记为守门员。如果它们都一样,我们就不会,所以第二个动作生效,如果不是守门员,我们就跳过队伍。第三个操作保存当前行并打印它,清除为下一行做好准备的保持标志。

我没有数百万行长的数据集,所以我不确定这是否适合您。尝试一下看看。

答案3

您只是在锚定到行开头的模式中寻找最长的唯一匹配,因此假设您的文件名为 tst ....

while read l ; do if [ $(grep -c -E "^$l" tst) -eq 1 ]; then echo $l; fi ; done < tst

然而,如果最长的模式重复,这将会失败,所以你需要处理这个......

while read l ; do if [ $(grep -c -E "^$l" <<<$(sort tst | uniq)) -eq 1 ]; then echo $l; fi ; done <<<$(sort tst | uniq)

相关内容