如果其内容与另一个文件匹配,则将其添加到“tail -f file”的输出行前面

如果其内容与另一个文件匹配,则将其添加到“tail -f file”的输出行前面

尝试将以下所有内容塞进标题很困难,所以我真正想说的是:

我正在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

相关内容