
Удаление дубликатов с помощью awk — довольно распространенная и простая операция. Но мне нужно вывести только те строки, которые дублируются, когда мы сравниваем только один столбец. Я попробовал эту команду:
awk 'seen[$2]++'
но как вы видите, у него есть недостатки. Он печатает дубликаты, но только с момента их второго появления. Я только начал привыкать к unix и bash, поэтому было бы здорово, если бы вы могли объяснить мне решение.
решение1
Я вижу два способа сделать это:
дважды пройдемся по файлу:
В первой итерации подсчитайте, сколько раз встречается каждое $2.
Во второй итерации выведите только те строки, где количество больше 1.awk 'NR == FNR {count[$2]++; next} count[$2] > 1' file file
с одной итерацией данных:
Вам нужно посчитать, сколько раз встречается каждая цифра $2,ипомните, какие очереди возникали за каждые 2 доллара.
Этот ответ использует GNU awk для массива массивов. Порядок вывода, скорее всего, не будет таким же, как у входных данных. Он также должен сохранить весь файл в памяти.
gawk ' { lines[$2][++count[$2]] = $0 } END { for (x in lines) if (count[x] > 1) for (i=1; i<=count[x]; i++) print lines[x][i] } ' file
Протестировано с входным файлом:
$ cat file
a b
b b
c b
a c
a d
b d
a e
и ожидаемый результат
a b
b b
c b
a d
b d
решение2
Используя тот же образец входных данных, что иГленн Джекманответ
$ awk '$2 in seen{if(c[$2]--){print fl[$2]} print} !seen[$2]++{fl[$2]=$0; c[$2]=1}' file
a b
b b
c b
a d
b d
!seen[$2]++
если$2
ранее не встречалось:fl[$2]=$0
сохраните эту первую строку, я предположил, что входные данные не сортируются и дубликаты могут встречаться в любом месте файла, поэтому сохраняю ее на основе,$2
а не просто временной переменнойc[$2]=1
Аналогично, инициализируйте переменную count значением 1
$2 in seen
если$2
произошло ранее:if(c[$2]--){print fl[$2]}
сначала выводится предыдущая строка, счетчик уменьшается, так что условие не будет выполнено для последующих совпаденийprint
затем распечатать текущую строку
С некоторыми другими вкладами
$ cat ip.txt
6.2 : 897 : bar
3.1 : 32 : foo
1.2 : 123 : xyz
2.3 : 32 : baz
7.5 : 897 : boo
$ awk -F: '$2 in seen{if(c[$2]--){print fl[$2]} print} !seen[$2]++{fl[$2]=$0; c[$2]=1}' ip.txt
3.1 : 32 : foo
2.3 : 32 : baz
6.2 : 897 : bar
7.5 : 897 : boo
Обратите внимание, что порядок зависит от того, как возникают дубликаты.
решение3
При повторном прохождении одного и того же файла можно использовать номера строк в качестве удобных индексов; это может обеспечить более ясную логику.
awk 'NR == FNR {if ($2 in z) { y[z[$2]]; y[FNR] } z[$2]=FNR; next} (FNR in y)' file file
Я использовал похожий прием в своем ответе на этот вопрос:
В основе этого трюка лежит то, что Awk создает переменную, просто ссылаясь на нее, а index in arrayname
конструкция возвращает значение true или false в зависимости от того, был ли создан элемент массива с указанным индексом.