web-dev-qa-db-ja.com

ログ出力でsedを使用してIPアドレスをホスト名に置き換えます

DnsmasqログファイルのIPアドレスをそのホスト名に置き換えようとしています。ログファイルは、コンソールでコマンド「tail -f/var/log/dnsmasq.log」を使用して「監視」されています。出力をsedにパイプして、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"

'regex'は、文字列 "query"を含む行を識別し、その行の終わりからIPアドレスを抽出して、変数に(何らかの方法で)格納する必要があります-ここでは$1という表記を使用しました-使用されています式をDigに置き換えます。

更新:IPアドレスは常に10.1.n.nの形式になることは言及しませんでした

2
deanodley

残念ながら、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
LL3

このちょっと、sortaは機能します(ただし、「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/{...}正規表現 'query'に一致する行で{...}を実行します(他の文字を出力するだけです)

IP = $ NF新しい変数「IP」を行の最後のフィールドの値(IPアドレス)に設定します

$ NF = ""行の最後のフィールドをザップします

L = $新しい変数「L」を残りの行に設定します(つまり、IPアドレスなし)

"Host" IP | getline name IPアドレスで 'Host'を実行し、結果を新しい変数 'name'に入れます

$ 0 = name次のコマンドで$ NFを使用できるように、現在の行を「Host」コマンドからの出力に設定します。

print L、$ NF「L」(IPアドレスのない入力行)と「Host」コマンドの最後のフィールド(ホスト名)を出力します。

1
wef

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'
_

これはシステムのネームサービスにクエリを実行しているため、おそらく_/etc/hosts_、DNS、mDNS、LDAP、NIS + ...、またはシステムの_/etc/nsswitch.conf_または同等のホスト名解決用に構成されているもので、おそらく名前を使用します。 nscdsssdのようなサービスキャッシングサービス。同じIPアドレスを何度も照会しないように、キャッシングも実装しています。

4つの_._で区切られた10進数のシーケンスのみを照合し、他のIPv4アドレス形式は照合しませんが、inet_aton()の場合、先頭の0は数値を8進数と見なすため、_010.010.010.010_は実際には_8.8.8.8_です(IPアドレスを引数として取るほとんどのものと同じですが、_Dig -x_は取りません)。

DigのようにのみDNSサーバーにクエリを実行する必要がある場合は、gethostbyaddr()の代わりに_Net::DNS_を使用できます。

_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