sed를 사용하여 로그 출력에서 ​​IP 주소를 호스트 이름으로 바꿉니다.

sed를 사용하여 로그 출력에서 ​​IP 주소를 호스트 이름으로 바꿉니다.

dnsmasq 로그 파일의 IP 주소를 호스트 이름으로 바꾸려고 합니다. 로그 파일은 "tail -f /var/log/dnsmasq.log" 명령을 사용하여 콘솔에서 "모니터링"되고 있으며 출력을 sed로 파이프하여 "Query"가 포함된 줄에서만 IP 주소를 바꾸려고 합니다. 호스트 이름 텍스트. IP 주소는 항상 이 줄의 끝에 있습니다.

예시 라인은 다음과 같습니다:

Apr  1 00:47:43 dnsmasq[1004]: query[A] gs-loc.apple.com from 10.1.1.188

나는 명령이 다음과 같은 형식이라고 생각합니다.

tail -f /var/log/dnsmasq.log | sed -e "s/'regex'/$(dig +short -x $1)/g"

"정규식"은 "query" 문자열이 포함된 행을 식별하고, 행 끝에서 IP 주소를 추출하여 (어쨌든) 변수에 저장해야 합니다. $1여기서는 표기법을 사용했습니다. 교체 표현식 dig에 사용되었습니다.

업데이트: IP 주소가 항상 10.1.nn 형식이라는 점을 언급하지 못했습니다.

답변1

불행하게도 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 ; \
    }

답변2

이것은 어느 정도 작동합니다(그러나 "sed" 대신 "awk"를 사용합니다):

$ 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=""zap 줄의 마지막 필드

L=$0새 변수 "L"을 나머지 행(즉, IP 주소 없음)에 설정합니다.

"호스트" IP 라인 이름 가져오기 |IP 주소에서 "host"를 실행하고 결과를 새 변수 "name"에 넣습니다.

$0=이름다음 명령에서 $NF를 사용할 수 있도록 현재 행을 "host" 명령의 출력으로 설정합니다.

인쇄L,$NF"L"(IP 주소가 없는 입력 줄)과 "host" 명령의 마지막 필드(호스트 이름)를 인쇄합니다.

답변3

각 IP 주소에 대해 실행하는 것은 dig매우 비효율적이며 DNS 서버의 부하를 증가시킵니다. 여기서는 다음을 사용 합니다 perl.

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

이것은 시스템의 이름 서비스를 쿼리하므로 DNS, mDNS, LDAP, NIS+... 또는 /etc/hosts이름 서비스 캐시 서비스를 통해 호스트 이름 확인 또는 이에 상응하는 시스템에 구성된 모든 것이 될 수 있습니다. 또한 동일한 IP 주소를 여러 번 쿼리하는 것을 방지하기 위해 구현되었습니다./etc/nsswitch.confnscdsssd

.우리는 다른 IPv4 주소 형식이 아닌 4개의 분리된 10진수 시퀀스만 일치시킵니다 . 그러나 의 inet_aton()경우 앞에 0이 있으면 숫자가 8진수로 처리되므로 010.010.010.010실제로 8.8.8.8는 IP 주소를 주장하지만 dig -x).

이와 같이 DNS 서버에 쿼리하는 데 필요한 경우 다음을 사용할 dig수 있습니다 .Net::DNSgethostbyaddr()

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'

관련 정보