尝试将以下所有内容塞进标题很困难,所以我真正想说的是:
我正在tail -F access.log
实时查看 Apache2 日志。每行的第一行是 IP 地址,后面跟着一个空格。我还有一个不变的 CSV 文件,其中每行都是 IP 地址、逗号和名称。例如:
10.1.2.33,John Smith
10.1.2.190,Jane Doe
可交付成果:我想像以前一样跟踪我的访问日志,但是如果 CSV 恰好包含与当前日志文件行开头相同的 IP 地址的行,则在该行的开头插入该人的姓名。
例如,如下一行:
10.1.2.33 - - [22/Aug/2013:13:41:24 +0000] "GET /index.php ...
应渲染为:
John Smith 10.1.2.33 - - [22/Aug/2013:13:41:24 +0000] "GET /index.php ...
理想情况下,我希望通过将一些正则表达式命令连接在一起来完成此操作。到目前为止,我最大的问题是 grep 不接受 stdin 上的模式,它期望 stdin 上的主题(除非我遗漏了什么)。此外,如果需要,我可以毫无问题地将 CSV 转换为更好的格式。谢谢。
答案1
这是一个 POSIX shell 解决方案
tail -f access.log | while read -r line;do
ip=${line%% *}
name=$(grep -F "$ip" your_csv_file|cut -d, -f2)
if [ -z "$name" ];then
printf "%s\n" "$line"
else
printf "%s\n" "$name $line"
fi
done
编辑
对解决方案做出两项改进:
-r
开关添加到,read
以便它不会评估它读取的行中的转义序列-F
开关添加到,grep
以便它将 IP 视为固定字符串而不是正则表达式。
答案2
我会用一个小的 Perl 脚本来做到这一点:
#!/usr/bin/env perl
## Open the first argument, the .csv file
open($f,"<","$ARGV[0]");
## Collect the list of ips and names in the %ips hash
while(<$f>){
chomp;
@a=split(/,/);
$ips{$a[0]}=$a[1]
}
## Now open the second argument, the log file to be watched. The
## trick here is to open by piping through tail -F
open($f,"-|", "tail -F $ARGV[1]");
while(<$f>){
## Get the ip
/^([\d\.]+)/;
$ip=$1;
## Prepend the associated name from the .csv file if one is defined
s/$ip/$ips{$ip} $ip/ if defined($ips{$ip});
## Print the line
print;
}
将其保存为您的 $PATH ip2name.pl
,使其可执行(chmod +x ip2name.pl
),然后像这样运行它:
ip2name.pl /home/foo/ips.csv /var/log/apache2/access.log
答案3
grep
可以从 stdin 获取模式,但不能同时从 stdin 获取文本。请参阅选项-f
。如果您的 shell 支持进程替换,您可以使用以下方式模拟它
produce_patterns | grep -f- <( produce_input )
例如:
( echo b; echo y ) | grep -f- <( for i in {a..z} ; do echo $i ; done )
输出:
b
y