Самый простой способ - алгоритм двойного прохода, при котором вы дважды читаете файл.
Идея состоит в том, чтобы сохранить все значения в массиве 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>
На практике вам следует избегать таких вещей, как a[var]==1
, если вы не уверены, находится ли var
в массиве, так как он создаст этот элемент массива. Однако, так как мы никогда больше не увеличиваем его, можно продолжать.
Если вы хотите достичь того же самого с более чем тремя полями, вы можете сделать:
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>
примечание: вы никогда не должны стремиться к самому короткому решению, кроме самого читаемого!