Pré-pendente para uma linha de saída de 'tail -f file' se seu conteúdo corresponder a outro arquivo

0

Tentar encaixar todos os itens a seguir no título foi difícil, então é isso que eu realmente quero dizer:

Estou fazendo um tail -F access.log para assistir a um log do Apache2 em tempo real. A primeira coisa em cada linha é um endereço IP seguido por um espaço. Eu também tenho um arquivo CSV imutável, onde cada linha é um endereço IP, vírgula, nome. Por exemplo:

10.1.2.33,John Smith e 10.1.2.190,Jane Doe

Entregável: desejo seguir meu log de acesso como antes, mas se o CSV contiver uma linha com o mesmo endereço IP com o qual a linha atual do arquivo de log começa, insira o nome da pessoa em o começo da linha.

Por exemplo, uma linha como esta:

10.1.2.33 - - [22/Aug/2013:13:41:24 +0000] "GET /index.php ...

Em vez disso, deve renderizar como:

John Smith 10.1.2.33 - - [22/Aug/2013:13:41:24 +0000] "GET /index.php ...

Idealmente, eu gostaria de fazer isso inteiramente canalizando alguns comandos regex juntos. Até agora, meu maior problema é que o grep não aceita um padrão em stdin, ele espera o assunto em stdin (a menos que eu esteja perdendo alguma coisa). Além disso, não tenho nenhum problema em transformar o CSV em algum formato melhor, se necessário. Obrigado.

    
por yeahforbes 22.08.2013 / 15:49

3 respostas

1

Aqui está uma solução shell POSIX

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

Editar

Duas melhorias feitas na solução:

  • -r switch adicionado a read para que não avalie sequências de escape nas linhas que lê
  • -F switch adicionado a grep para que ele trate o IP como uma string fixa em vez de uma expressão regular.
por 22.08.2013 / 18:03
1

Eu faria isso com um pequeno script 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;
}

Salve isso em seu $ PATH como ip2name.pl , torne-o executável ( chmod +x ip2name.pl ) e execute-o assim:

ip2name.pl /home/foo/ips.csv /var/log/apache2/access.log
    
por 22.08.2013 / 16:46
0

grep pode obter os padrões de stdin, mas não pode obter o texto de stdin ao mesmo tempo. Veja a opção -f . Se o seu shell suporta a substituição de processos, você pode simulá-lo com

produce_patterns | grep -f- <( produce_input )

Por exemplo:

( echo b; echo y ) | grep -f- <( for i in {a..z} ; do echo $i ; done )

Saída:

b
y
    
por 22.08.2013 / 15:53