Удаление строк, содержащих уникальное первое поле с помощью awk? - PullRequest
4 голосов
/ 26 февраля 2011

Нужно печатать только те строки, которые имеют повторяющееся первое поле. например из данных, которые выглядят так:

1 abcd
1 efgh
2 ijkl
3 mnop
4 qrst
4 uvwx

Следует распечатать:

1 abcd
1 efgh
4 qrst
4 uvwx

(К вашему сведению - первое поле не всегда длиной 1 символ в моих данных)

Ответы [ 5 ]

5 голосов
/ 26 февраля 2011
awk 'FNR==NR{a[$1]++;next}(a[$1] > 1)' ./infile ./infile

Да, вы даете ему тот же файл, что и ввод дважды. Поскольку вы не знаете заранее, является ли текущая запись уникальной или нет, вы строите массив на основе $1 при первом проходе, тогда вы выводите только те записи, которые видели $1 более одного раза на втором проходе. .

Я уверен, что есть способы сделать это только с одним проходом через файл, но я сомневаюсь, что они будут такими же "чистыми"

Объяснение

  1. FNR==NR: Это верно только тогда, когда awk читает первый файл. По сути, он проверяет общее количество просмотренных записей (NR) по сравнению с входной записью в текущем файле (FNR).
  2. a[$1]++: создание ассоциативного массива a , ключ которого является первым полем ($1), а значение которого увеличивается на единицу каждый раз, когда его видят.
  3. next: игнорировать оставшуюся часть сценария, если это будет достигнуто, начать заново с новой входной записи
  4. (a[$1] > 1) Это будет оцениваться только на втором проходе ./infile и печатать только те записи, кто первое поле ($1) мы видели не раз. По сути, это сокращение для if(a[$1] > 1){print $0}

Подтверждение концепции

$ cat ./infile
1 abcd
1 efgh
2 ijkl
3 mnop
4 qrst
4 uvwx

$ awk 'FNR==NR{a[$1]++;next}(a[$1] > 1)' ./infile ./infile
1 abcd
1 efgh
4 qrst
4 uvwx
1 голос
/ 26 февраля 2011
BEGIN { IDLE = 0; DUP = 1; state = IDLE }

{ 
  if (state == IDLE) {
    if($1 == lasttime) {
       state = DUP
       print lastline
    } else state = IDLE
  } else {
    if($1 != lasttime)
        state = IDLE
  }
  if (state == DUP)
    print $0
  lasttime = $1
  lastline = $0
}
1 голос
/ 26 февраля 2011

Вот некоторый awk-код, который делает то, что вы хотите, предполагая, что вход сгруппирован по его первому полю (например, uniq также требует):

BEGIN {f = ""; l = ""}
{
  if ($1 == f) {
    if (l != "") {
      print l
      l = ""
    }
    print $0
  } else {
    f = $1
    l = $0
  }
}

В этом коде f - этопредыдущее значение поля 1 и l является первой строкой группы (или пустым, если она уже была распечатана).

0 голосов
/ 26 февраля 2011

Если вы можете использовать Ruby (1.9 +)

#!/usr/bin/env ruby
hash = Hash.new{|h,k|h[k] = []}
File.open("file").each do |x|
  a,b=x.split(/\s+/,2)
  hash[a] << b
end
hash.each{|k,v| hash[k].each{|y| puts "#{k} #{y}" } if v.size>1 }

выход:

$ cat file
1 abcd
1 efgh
2 ijkl
3 mnop
4 qrst
4 uvwx
4 asdf
1 xzzz

$ ruby arrange.rb
1 abcd
1 efgh
1 xzzz
4 qrst
4 uvwx
4 asdf
0 голосов
/ 26 февраля 2011

Предполагая упорядоченный ввод, как показано в вопросе:

awk '$1 == prev {if (prevline) print prevline; print $0; prevline=""; next} {prev = $1; prevline=$0}' inputfile

Файл необходимо прочитать только один раз.

...