我正在尝试用主机名替换 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"
“正则表达式”需要识别包含字符串“query”的行,从该行末尾提取 IP 地址并将其(以某种方式)存储在变量中 - 我$1
在这里使用了符号 - 在替换表达式中使用挖。
更新:我没有提及 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
这有点有效(但使用“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 命令的解释:
/询问/{ ... }在与正则表达式“查询”匹配的行上执行 {...} (仅打印其他行)
IP=$NF将新变量“IP”设置为该行最后一个字段的值(IP 地址)
$NF=“”zap 线上的最后一个字段
L=$0将新变量“L”设置为剩余行(即没有 IP 地址)
“主机”IP |获取线路名称在 IP 地址上运行“host”并将结果放入新变量“name”中
$0=姓名将当前行设置为“host”命令的输出,以便我们可以在下一个命令中使用 $NF。
打印 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.conf
nscd
sssd
我们只匹配 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'