Awk 如果发现错误,请删除每一行

Awk 如果发现错误,请删除每一行,awk,sed,cat,Awk,Sed,Cat,我有一个包含以下内容的文件格式: 1 6 8 1 6 9 1 12 20 1 6 2 8 2 9 2 12 2 20 2 35 如果在下一行中找到编号(从第2列或第3列,但不是从第1列),我想删除所有行,无论它是在第2列还是第3列,包括找到初始编号的行 我应该将此作为输出: 2 35 我试过使用: awk '{for(i=2;i<=NF;i++){if($i in a){next};a[$i]}} 1' awk'{for(i=2;i请您尝试以下内容 awk '

我有一个包含以下内容的文件格式:

1  6  8
1  6  9
1  12 20
1  6
2  8
2  9
2  12
2  20
2  35
如果在下一行中找到编号(从第2列或第3列,但不是从第1列),我想删除所有行,无论它是在第2列还是第3列,包括找到初始编号的行

我应该将此作为输出:

2 35
我试过使用:

awk '{for(i=2;i<=NF;i++){if($i in a){next};a[$i]}} 1' 

awk'{for(i=2;i请您尝试以下内容

awk '
FNR==NR{
  for(i=2;i<=NF;i++){
    a[$i]++
  }
  next
}
(NF==2 && a[$2]==1) || (NF==3 && a[$2]==1 && a[$3]==1)
'  Input_file  Input_file

一次传递awk,它将所有记录散列到
r[NR]
,并为字段
$2,.NF
中的值保留另一个数组
a[$i]

awk ' {
    for(i=2;i<=NF;i++)       # iterate fields starting from the second
        if($i in a) {        # if field value was seen before
            delete r[a[$i]]  # delete related record
            a[$i]=""         # clear a
            f=1              # flag up
        } else {             # if it was not seen before
            a[$i]=NR         # add record number to a
            r[NR]=$0
        }
    if(f!=1)                 # if flag was not raised
        r[NR]=$0             # store record on record number
    else                     # if it was raised
        f=""                 # flag down
}
END {
    for(i=1;i<=NR;++i)
        if(i in r)
            print r[i]       # output remaining
}' file

最简单的方法是双通道算法,在该算法中,您将文件读取两次

其思想是将所有值存储在一个数组
a
中,并计算它们出现的次数。如果该值出现2次或更多次,则表示您已找到多个条目,不应打印该行

awk '(NR==FNR){a[$2]++; if(NF>2) a[$3]++; next} 
     (NF==2) && (a[$2]==1);
     (NF==3) && (a[$2]==1 && a[$3]==1)' <file> <file>
虽然这两种解决方案都会读取文件两次,但您也可以将完整的文件存储在内存中,并且只读取一次文件。但是,这是完全相同的算法:

awk '{for(i=2;i<=NF;++i) a[$i]++; b[NR]=$0}
     END{ for(j=1;j<=NR;++j) {
            $0=b[j];
            for(i=2;i<=NF;++i) if(a[$i]>1) continue
            print $0
          }
         }' <file>
awk'{for(i=2;i
$cat tst.awk
NR==FNR{
cnt[$2]++
cnt[$3]++
下一个
}
cnt[$2]这可能适合您(GNU-sed):

这不是一个严肃的解决方案,但是它演示了仅使用匹配和替换即可实现的功能

解决方案制作原始文件的副本,同时将每个记录的第二个和可能的第三个字段中的所有数字累加到一个单独的行中,并保存在副本的开头


在文件末尾,副本的第一行包含所有相关键,如果存在重复键,则删除文件中包含此类键的任何行。这可以通过将键(第一行)移动到文件末尾并匹配第二行(可能还有第三行)来实现这些键上每条记录的字段。

OP应该解释“在下一行中找到”。它是指“在下一行中找到”吗?如果为真,则只应删除示例中的前3行。您的示例不起作用的是什么,您得到的输出是什么。例如:第一行包含6和8,这些数字也可以在第2行、第4行和第5行中找到。因此,应删除第1行、第2行、第4行和第5行等。在这种情况下,只应保留最后一行,即(2,35)也许我做错了什么,但它没有给我任何输出。@Inours,首先我要读取输入文件2次,所以请确保您复制的代码正确,然后如果您的输入文件与所示相同,那么示例+
35
不会多次进入您的输入文件,那么这应该可以工作,如果不是这样,请检查您的输入是否正确_通过执行
cat-v输入文件
来控制文件中的M个字符,然后告诉我???@RavinderSingh13它对我有效。我忘记放置输入文件两次。如果
a[$2]==1
它将打印该行,并且永远不会测试
a[$3]==1
。理论上,您应该使用
&
而不是
|
,但由于字段的数量不同,这将失败。@kvantour,当然,现在更改了代码,感谢您让我知道。出于好奇,如果您的数据足够大,并且您测试了所有这些解决方案,请告诉我是否有一个或两个通过的解决方案非常好的解决方案。我喜欢清除缓冲区以减少内存。但是,考虑到
for(I in r)
将以未指定的顺序迭代,因此您可能无法保持顺序不变。您可能需要为编写
(i=1;我对这个标志有点困惑。它有什么用?它看起来像
if的
else
语句($a中的i)
已经完成了所有的工作。还是我遗漏了什么?这非常聪明,因为它使用了两个漂亮的想法!1.无论如何都要添加第三个字段。因此,如果只有两个字段,请增加空字段的计数器!2.使用
cnt[$NF]
而不是
cnt[$3]
这确保了如果您只有2个字段,您只需测试字段
$2
两次!(如果可以,我会投票10次)谢谢。IMHO
NF
是一个未被充分利用的资源。使用
NF
$NF
而不是其他方法可以轻松解决许多问题,例如。
awk '(NR==FNR){a[$2]++; if(NF>2) a[$3]++; next} 
     (NF==2) && (a[$2]==1);
     (NF==3) && (a[$2]==1 && a[$3]==1)' <file> <file>
awk '(NR==FNR){for(i=2;i<=NF;++i) a[$i]++; next }
     {for(i=2;i<=NF;++i) if(a[$i]>1) next }
     {print}' <file> <file>
awk '{for(i=2;i<=NF;++i) a[$i]++; b[NR]=$0}
     END{ for(j=1;j<=NR;++j) {
            $0=b[j];
            for(i=2;i<=NF;++i) if(a[$i]>1) continue
            print $0
          }
         }' <file>
awk '{ for(i=2;i<=NF;++i) if ($i in a) delete b[a[$i]]; else { a[$i]=NR; b[NR]=$0 }}
     END { for(n=1;n<=NR;++n) if(n in b) print b[n] }' <file>
$ cat tst.awk
NR==FNR {
    cnt[$2]++
    cnt[$3]++
    next
}
cnt[$2]<2 && cnt[$NF]<2

$ awk -f tst.awk file file
2  35
sed -r 'H;s/^[0-9]+ +//;G;s/\n(.*\n)/\1/;h;$!d;s/^([^\n]*)\n(.*)/\2\n  \1/;:a;/^[0-9]+ +([0-9]+)\n(.*\n)*[^\n]*\1[^\n]*\1[^\n]*$/bb;/^[0-9]+ +[0-9]+ +([0-9]+)\n(.*\n)*[^\n]*\1[^\n]*\1[^\n]*$/bb;/\n/P;:b;s/^[^\n]*\n//;ta;d' file