Используйте sed для замены IP-адреса на имя хоста в выводе журнала

Я думаю, что find вполне подходит для этой

находки. -тип d! -regex '^. *)' -ls

-regex '^.*) 'соответствует всем именам, оканчивающимся скобкой,! инвертирует это, - опция типа d предназначена для каталогов, - ls (необязательно) делает вывод похожим на ls

2
01.04.2019, 22:32
3 ответа

К сожалению, sed не может запускать внешние команды, одновременно передавая параметры, взятые из входных данных.

Это решение сценария Bash, которое вам подойдет:

tail -f dnsmasq.log | { while IFS= read -r line ; do { [[ "${line}" =~ ": query[A]" ]] && printf '%s %s\n' "${line% *} " $(dig +short -x "${line##* }"); } || echo "${line}"; done ; }

Разбито для объяснения:(только для ясности, может не работать при копировании и вставке)

tail -f dnsmasq.log | \
    { \
        while IFS= read -r line ; do \           # for each line read in from tail...
            if [[ "${line}" =~ ": query[A]" ]] ; # if it has the literal string ': query[A]'
            then \
                printf '%s %s\n' "${line% *} " \ # print it (purged of last field, which is the IP address)...
                $(dig +short -x "${line##* }") \ # along with dig's output
            else \                               # otherwise...
                echo "${line}" \                 # just print it all as it is
            fi \
        done ; \
    }
1
27.01.2020, 22:08

Это вроде как работает (, но используется «awk» вместо «sed»):

$ echo $'Apr  1 00:47:43 dnsmasq[1004]: query[A] gs-loc.apple.com from 8.8.8.8' | awk '/query/{ IP=$NF; $NF=""; L=$0; "host " IP | getline name; $0=name; print L,$NF }'
Apr 1 00:47:43 dnsmasq[1004]: query[A] gs-loc.apple.com from  google-public-dns-a.google.com.

... нужно немного доработать, например, если поиск хоста не работает; возможно, «запрос» регулярного выражения должен быть немного более конкретным.

Вот объяснение команды awk:

/запрос/{...} выполнить {...} в строках, соответствующих регулярному выражению 'query' (просто напечатать другие)

IP=$NF установить новую переменную 'IP' на значение последнего поля в строке (IP-адрес)

$NF="" удалить последнее поле в строке

L=$0 установить новую переменную 'L' в оставшуюся строку (т.е. без IP-адреса)

"хост" IP | getline name запустите 'host' по IP-адресу и поместите результат в новую переменную 'name'

$0=имя установить текущую строку для вывода команды 'host', чтобы мы могли использовать $NF в следующей команде.

print L,$NF Print 'L' (строка ввода без IP-адреса )и последнее поле команды host (имя хоста ).

1
27.01.2020, 22:08

Запуск digдля каждого IP-адреса будет очень неэффективным и добавит нагрузку на ваш DNS-сервер. Я бы использовал perlздесь:

perl -MSocket -pe 's{(?<![\d.])\d+\.\d+\.\d+\.\d+(?![\d.])}{
    $ip = inet_aton($&);
    $cache{$ip} //= gethostbyaddr($ip,AF_INET) // "UNKNOWN[$&]"
  }ge'

Это запрашивает службу имен вашей системы, поэтому, возможно, /etc/hosts, DNS, mDNS, LDAP, NIS+... или что-то другое, настроенное для разрешения имен хостов в /etc/nsswitch.confили эквивалентное в вашей системе, возможно, через службу имен службы кэширования, такие как nscdили sssd, и мы также реализуем наше кэширование, чтобы избежать повторного запроса одного и того же IP-адреса.

Мы сопоставляем только последовательности из 4.-разделенных десятичных чисел, а не другие форматы адресов IPv4, но обратите внимание, что для inet_aton()начальные 0 заставляют числа считаться восьмеричными, поэтому 010.010.010.010на самом деле8.8.8.8(то же, что и для большинства вещей, которые принимают IP-адреса в качестве аргументов, но неdig -x).

Если вам нужно запрашивать DNS-сервер только так, как это делает dig, вы можете использовать Net::DNSвместоgethostbyaddr():

perl -MNet::DNS -pe '
  sub resolve {
    my ($r) = rr($_[0]);
    if (defined($r)) {
      return $r->ptrdname;
    } else {
      return "UNKNOWN[$_[0]]";
    }
  }
  s{(?<![\d.])\d+\.\d+\.\d+\.\d+(?![\d.])}{$cache{$&} //= resolve $&}ge'
0
27.01.2020, 22:08

Теги

Похожие вопросы