
Bash: Пример оптимизации команд фильтрации с grep и awk
Приветствую!
Расскажу про одну задачку, которую решал на днях🧑💻 Вводные данные я немного видоизменил, но суть осталась та же.
Подписывайтесь на наш телеграм @r4ven_me📱, чтобы не пропустить новые публикации на сайте😉. А если есть вопросы или желание пообщаться по тематике — заглядывайте в Вороний чат @r4ven_me_chat🧐. |
Имеется два csv файла:
📄ping_status.csv
— содержит дигностическую информацию о доступности хостов по пингу.
Пример:
ID|DNS_NAME|IP_ADDRESS|PING_STATUS
1|host1.corp.local|10.66.163.62|success
2|host2.corp.local|10.20.202.136|fail
3|host3.corp.local|10.251.60.85|success
4|host4.corp.local|10.137.229.129|success
5|host5.corp.local|10.161.196.50|success
6|host6.corp.local|10.214.27.115|fail
7|host7.corp.local|10.188.87.205|fail
8|host8.corp.local|10.2.203.4|success
9|host9.corp.local|10.8.74.100|fail
10|host10.corp.local|10.21.116.174|success
📃device_list.csv
— список устройств с указанием подразделения и типа.
Пример:
DEPARTMENT;IP_ADDRESS;DEVICE_TYPE
QA;10.92.104.3;workstation
HR;10.25.101.192;server
DevOps;10.242.234.48;workstation
DevOps;10.35.10.218;switch
Legal;10.196.97.105;printer
DevOps;10.105.56.232;router
Support;10.34.3.56;printer
IT;10.40.93.15;router
IT;10.72.223.200;laptop
Legal;10.60.206.98;switch
Задача: необходимо отобрать хосты с неуспешным статусом диагностики (поле PING_STATUS) из файла ping_status.csv
и проверить их наличие в файле device_list.csv
для подразделения DevOps (поле DEPARTMENT):
Немного поразмыслив составил такой однострочник:
while IFS= read -r line; do [ -n "$line" ] && grep -F -w -- "$line" /tmp/device_list.csv | awk -F ';' '$1 == "DevOps"'; done < <(awk -F '|' '$4 != "success" {print $3}' /tmp/ping_status.csv) | sort -u
По хорошему стоило написать полноценный скрипт, но нужно было оперативное решение да и стало интересно составить однострочник.
Команда представляет собой цикл while
, который построчно считывает псевдо файл (конструкция <(..)
), который является выводом другой команды — awk
, которая в свою очередь задает в качестве разделителя |
, отбирает устройства, у которых статус доступности не равно success и выводит только 3 столбец — IP адрес из файла ping_status.csv
. Затем с помощью grep выполняется поиск совпадений в файле device_list.csv
.
Опытный shell-разработчик, в отличие от меня, сразу увидит говнокод. Проблема заключается в том, что grep выполняется для каждой соответствующей условию строки из ping_status.csv
в device_list.csv
. Ничего страшного, если у вас устройств пару десятков или сотен. Это рабочий способ решить задачу. Но если речь идет о сотнях тысяч (мой случай), то столько раз выполнять grep — роскошь непозволительная. На обработку уйдет очень много времени.
Для примера:device_list.csv
— 300000 строкping_status.csv
— 1000000 строк
Время отработки команды выше при таких условиях будет: 29 минут, 17 секунд 😵
Не вариант.
Т.к. данную процедуру мне необходимо было проводить не один раз и с разными параметрами, решил оптимизировать команду, в т.ч. с помощью нейронки. В итоге получилось сформировать 2 варианта: с помощью grep
и awk
:
1) Быстрый вариант с grep
(в один проход) + awk
:
grep -F -w -f <(awk -F'|' '$4 != "success" && $4 ~ /[^[:space:]]/ {print $3}' /tmp/ping_status.csv) /tmp/device_list.csv | awk -F ';' '$1 == "DevOps"' | sort -u
Команда отработала за: 1.8 секунды🤯
Тут вся суть в использовании ключа -f
, который позволяет использовать в качестве входного шаблона указанный файл (в моем случае псевдофайл), обрабатывая его построчно.
Ну а [^[:space:]]
— это регулярка, позволяющее исключить пустые строки.
2) Быстрый вариант только с awk:
awk -F'|' '
FNR==NR && $4 != "success" && $4 ~ /[^[:space:]]/ {ips[$3]; next}
FNR!=NR && $1 == "DevOps" && $2 in ips {seen[$0]++}
END {for (line in seen) print line}
' /tmp/ping_status.csv FS=';' /tmp/device_list.csv
Команда отработала за: 1.7 секунды🤔
☝️Синтаксис
awk
для рептилоидов.
Тут чисто синтаксис и возможности awk
, на котором можно писать полноценные программы по обработке данных. Рекомендую запросить пояснения синтаксиса у нейронки.
В общем, результат на лицо😒
Если вдруг у вас есть идеи, как решить задачу более элегантно, то напишите свой вариант в комментариях👍
И берегите своё время👨💻 Хорошего дня!