AWK фильтрует первую и последнюю строки заданной переменной, отбрасывает средние строки

#unix #awk #filtering

#unix #awk #фильтрация

Вопрос:

Я пытаюсь отфильтровать файл, выбрав первую и последнюю строки заданной переменной в текстовом файле с разделителями табуляции, используя AWK.

Файл с разделителями табуляции выглядит следующим образом:

 1 apple  30
2 apple  35
3 apple  36
4 apple  20
5 pear   10
6 pear   30
7 pear   45
8 orange 16 

END 
  

и я пытаюсь обработать это с помощью awk, чтобы печатать только первую и последнюю строки каждой переменной в $ 2 (столбец fruit в этом примере)

Файл, который у меня на самом деле есть, имеет длину ~ 35000 строк и содержит 3000 уникальных переменных в столбце, который я хочу использовать в качестве фильтра (так, в приведенном выше примере col2)

Я думал, что подход будет заключаться в создании массива уникальных значений col2 (яблоко, груша, апельсин), а затем, используя этот массив, извлекает первое и последнее значения из файла большего размера… но некоторые рекомендации по номенклатуре, необходимой для выбора первой и последней строки для каждой индексированной переменной, были бы весьма признательны. 🙂

ВХОДНОЙ файл, указанный выше, ожидаемый результат будет

 1 apple  30
4 apple  20
5 pear   10
7 pear   45
8 orange 16
  

выходные данные также должны включать те, у которых есть только одна запись (в данном случае оранжевая)

Комментарии:

1. Отсортирован ли файл по столбцу 2?

2. Привет, да, файл отсортирован по столбцу 2.

Ответ №1:

Один из способов:

 awk '$2!=prev{if (pline){print pline;}print;}{prev=$2;pline=$0;}END{print pline;}' file | uniq
  

Печатайте каждый раз, когда встречается новая строка 2-го столбца. При печати новой строки 2-го столбца, если предыдущая строка не пуста, распечатайте и ее. uniq заключается в удалении повторяющихся строк, которые печатаются в случае наличия единственной записи между ними.

Ответ №2:

Это будет работать, даже если одни и те же данные отображаются как в первой, так и в последней строке для заданного значения ключа или если данные содержат пустые или 0 строки (при условии, что вы хотите, чтобы они обрабатывались так же, как и любая другая строка, легко пропускаются, если нет):

 $ cat tst.awk
$2 != prev2 {
    if ( NR > 1 ) {
        print rec
    }
    beg = rec = $0
    prev2 = $2
    next
}
{ rec = beg ORS $0 }
END { print rec }

$ awk -f tst.awk file
1 apple  30
4 apple  20
5 pear   10
7 pear   45
8 orange 16
  

Ответ №3:

пробовал на gnu awk, внешняя программа не нужна

 awk '{if($0~/^[a-z0-9]/) a[NR]=$0} END{f=1;asort(a); for(;i  <NR;){split(a[i],b);if(b[2]==$2||f){$1=b[1];$2=b[2];$3=b[3];if(f){f=0;print}} else if(b[2]){print;print b[1],($2=b[2]),b[3]}} }' d