Использование awk для записи только дубликатов

Использование awk для записи только дубликатов

Удаление дубликатов с помощью awk — довольно распространенная и простая операция. Но мне нужно вывести только те строки, которые дублируются, когда мы сравниваем только один столбец. Я попробовал эту команду:

awk 'seen[$2]++'

но как вы видите, у него есть недостатки. Он печатает дубликаты, но только с момента их второго появления. Я только начал привыкать к unix и bash, поэтому было бы здорово, если бы вы могли объяснить мне решение.

решение1

Я вижу два способа сделать это:

  1. дважды пройдемся по файлу:

    В первой итерации подсчитайте, сколько раз встречается каждое $2.
    Во второй итерации выведите только те строки, где количество больше 1.

    awk 'NR == FNR {count[$2]++; next} count[$2] > 1' file file
    
  2. с одной итерацией данных:

    Вам нужно посчитать, сколько раз встречается каждая цифра $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 в зависимости от того, был ли создан элемент массива с указанным индексом.

Связанный контент