Awk: удаление повторяющихся строк без сортировки после соответствия условиям

У меня есть список устройств, которые мне нужны для удаления дубликатов (сохраняйте только первое вхождение), сохраняя при этом порядок и соответствие условию. В этом случае я ищу определенную строку, а затем печатаю поле с именем устройства. Вот пример необработанных данных из приложения sar:

10:02:01 AM       sdc      0.70      0.00      8.13     11.62      0.00      1.29      0.86      0.06
10:02:01 AM       sda      0.00      0.00      0.00      0.00      0.00      0.00      0.00      0.00
10:02:01 AM       sdb      0.00      0.00      0.00      0.00      0.00      0.00      0.00      0.00
Average:          sdc      1.31      3.73     99.44     78.46      0.02     17.92      0.92      0.12
Average:          sda      0.00      0.00      0.00      0.00      0.00      0.00      0.00      0.00
Average:          sdb      0.00      0.00      0.00      0.00      0.00      0.00      0.00      0.00
10:05:01 AM       sdc      2.70      0.00     39.92     14.79      0.02      5.95      0.31      0.08
10:05:01 AM       sda      0.00      0.00      0.00      0.00      0.00      0.00      0.00      0.00
10:05:01 AM       sdb      0.00      0.00      0.00      0.00      0.00      0.00      0.00      0.00
10:06:01 AM       sdc      0.83      0.00     10.00     12.00      0.00      0.78      0.56      0.05
11:04:01 AM       sda      0.00      0.00      0.00      0.00      0.00      0.00      0.00      0.00
11:04:01 AM       sdb      0.00      0.00      0.00      0.00      0.00      0.00      0.00      0.00
Average:          sdc      0.70      2.55      8.62     15.91      0.00      1.31      0.78      0.05
Average:          sda      0.12      0.95      0.00      7.99      0.00      0.60      0.60      0.01
Average:          sdb      0.22      1.78      0.00      8.31      0.00      0.54      0.52      0.01

Следующее даст мне список устройств из строк, содержащих слово «средний», но он сортирует вывод:

sar -dp | awk '/Average/ {devices[$2]} END {for (device in devices) {print device}}'
sda
sdb
sdc

Следующее дает мне именно то, что я хочу (команда из здесь ):

sar -dp | awk '/Average/ {print $2}' | awk '!devices[$0]++'
sdc
sda
sdb

Может быть, я упускаю что-то до боли очевидное, но я не могу понять, как сделать то же самое в одной команде awk, то есть без передачи вывода первого awk во второй awk.


person crashmaxed    schedule 17.07.2014    source источник


Ответы (2)


Ты можешь сделать:

sar -dp | awk '/Average/ && !devices[$2]++ {print $2}' 
sdc
sda
sdb

Проблема в этой части for (device in devices). По какой-то причине for рандомизирует вывод.
Я прочитал длинную сложную информацию о том, почему кое-где, но не имею ссылки.

person Jotne    schedule 17.07.2014
comment
Насколько я знаю, awk не претендует на порядок извлечения ключей из массива. Хотя в awk 4 вы можете сообщить ему о сортировке, которую следует использовать при извлечении ключей (но я не знаю, возможен ли порядок ввода). - person Etan Reisner; 17.07.2014
comment
Для повышения эффективности массивы Awk хранятся в виде хеш-таблиц. Оператор in извлекает элементы из массива в том порядке, в котором они хранятся в памяти, т. е. в том порядке, в котором их упорядочивает алгоритм хеширования. Если вам нужен массив, пройденный в определенном порядке, вам нужно решить, какой порядок (порядок вставки? алфавитный? числовой? по элементу? по индексу? что-то еще?) и каким-то образом запрограммировать этот порядок. С помощью GNU awk вы можете назначить порядок, заполнив PROCINFO["sorted_in"], см. gnu.org/software/gawk/manual/gawk.html#Scanning-an-Array. - person Ed Morton; 17.07.2014
comment
@EdMorton Спасибо за освежение. Память у меня ограничена, и по какой-то причине я стал удалять вещи сам, не сказав мне :) Это ссылка на sorted_in gnu.org/software/gawk/manual/ - person Jotne; 17.07.2014
comment
@Jotne расскажи мне об этом. Я выучил французский в школе, а несколько лет назад начал изучать испанский, который, как я в конце концов понял, просто вытеснил французский из моего мозга, чтобы освободить место. В результате я теперь не могу говорить ни на одном из них и едва владею английским… - person Ed Morton; 17.07.2014

awk '/Average/ && !devices[$2]++ {print $2}' sar.in

Вам просто нужно совместить два теста. Единственное предостережение заключается в том, что в оригинале вся строка представляет собой поле два из исходного ввода, поэтому вам нужно заменить $0 на $2.

person Etan Reisner    schedule 17.07.2014
comment
Очень похоже на мой пост :) - person Jotne; 17.07.2014