Comparando 3 linhas e 2 linhas no BASH

2

Eu tenho um arquivo de texto com esta aparência:

2014-11-24 12:59:42.169 101.0.0.0 source
2014-11-24 12:59:40.375 104.156.80.0 destination
2014-11-24 12:59:36.729 104.219.48.0 destination
2014-11-24 12:59:40.377 104.37.160.0 source
2014-11-24 12:58:58.902 107.188.128.0 both
2014-11-24 12:59:06.456 107.188.128.0 source
2014-11-24 12:59:06.840 107.192.0.0 both
2014-11-24 12:59:42.043 107.192.0.0 destination
2014-11-24 12:58:58.904 107.192.0.0 source
2014-11-24 12:59:55.488 111.0.0.0 both
2014-11-24 12:59:30.007 111.0.0.0 destination
2014-11-24 12:59:33.209 108.175.32.0 destination
2014-11-24 12:59:06.841 108.175.32.0 source
  • Para o IP 107.188.128.0, que é marcado como ambos e source , eu só quero marcá-lo como ambos .
  • Para o IP 107.192.0.0, que é marcado como destino e origem , só quero marcá-lo como ambos .
  • Para o IP 111.0.0.0, que é marcado como both e destination , quero marcá-lo como ambos .
  • Para o IP 107.192.0.0, que é marcado como destino e origem , só quero marcá-lo como ambos .

Meu resultado desejado deve ser assim:

2014-11-24 12:59:42.169 101.0.0.0 source
2014-11-24 12:59:40.375 104.156.80.0 destination
2014-11-24 12:59:36.729 104.219.48.0 destination
2014-11-24 12:59:40.377 104.37.160.0 source
2014-11-24 12:59:06.456 107.188.128.0 both
2014-11-24 12:59:42.043 107.192.0.0 both
2014-11-24 12:59:55.488 111.0.0.0 both
2014-11-24 12:59:33.209 108.175.32.0 both

em que a data e a hora mais recentes correspondentes ao IP são emitidas.

Isso é o que eu tentei:

awk '{print $3}' input.txt | sort -u | while read line

do 
grep $line input.txt | head -1 
done

Mas, não funciona com o IP 108.175.32.0.

E esta solução:

  sed '
      N
      s/\([0-9.]\)\s\S\+\n.*\s\S\+$/ both/
      t
      P
      D
      ' input.txt

Mas isso funciona apenas com 108.175.32.0.

É possível obter a saída desejada de uma só vez usando awk ou sed ? Estou terrivelmente preso neste momento.

    
por Swatesh Pakhare 07.06.2016 / 19:29

6 respostas

1

A pergunta é semelhante a aqui com uma pequena modificação:

| sed '
    :1
    N    #add next line
    s/\([0-9.]\+\)\s\S\+\n.*\s\s\S\+$/ both/
    t1   #go to point 1 if exchange took place
    P    #print first line from two
    D    #remove first line, return to start
    '
    
por 10.06.2016 / 09:10
1

Isso faz o que você quer:

 awk 'BEGIN{ip="nothing" 
    time=""
    type=""
 }
 {
    # if the currently processed ip is not the same as the line 
    # being processed then we need to print the data.
    if (ip != $3)
    {
       # if ip == nothing then this is the first line do not print.
       # otherwise we are at a line with a new ip and we should print
       # the data saved from previous lines.
       if(ip != "nothing")
       { 
          print time, ip, type
       }
    # Remove the time update line since we are now doing it outside the
    # if statement so it always updates the time. This will make the 
    # outputted line print the last time stamp for each IP.
    #time=$1" "$2
    ip=$3
    type=$4
    }
    else if (type != $4)
    {
       type="both"
    }
    # no matter what update the time stamp value so that the latest
    # time stamp is kept for any given ip. Putting it after the if
    # that handles when a new ip is found, makes sure that it does not
    # override the value printed for the old ip line.
    time=$1" "$2
 }
 END{
    # Once we reach the end of the input, we still have 
    # the last set of values to print.
    print time, ip, type
 }'

Ele irá ler o arquivo e se houver duas linhas consecutivas com o mesmo ip e tipo diferente (des, src, ambos) ele irá mudar o tipo para ambos caso contrário se for encontrado um novo ip nos dados, ele imprimirá o tipo que tinha ..

    
por 07.06.2016 / 19:58
1

Dado o arquivo de entrada foo.txt :

  1. sort os primeiros três campos numericamente,
  2. use datamash para fazer o trabalho real de combinar tags de IP,
  3. cut um campo redundante,
  4. use sed para substituir qualquer tag combinada por "both".

    sort -r -k1n -k2n -k3n foo.txt | \
      datamash -W -f -s -g3 collapse 4 | \
      cut --complement -f4 | \
      sed 's/\t[sdb].*,.*$/\tboth/g'
    

Saída:

2014-11-24  12:59:42.169    101.0.0.0       source
2014-11-24  12:59:40.375    104.156.80.0    destination
2014-11-24  12:59:36.729    104.219.48.0    destination
2014-11-24  12:59:40.377    104.37.160.0    source
2014-11-24  12:59:06.456    107.188.128.0   both
2014-11-24  12:59:42.043    107.192.0.0     both
2014-11-24  12:59:33.209    108.175.32.0    both
2014-11-24  12:59:55.488    111.0.0.0       both
    
por 09.06.2016 / 07:39
0

Se você corrigir o erro de digitação destinatiion no arquivo de entrada (ou modificar o script para usar destinati?ion para que ele lida com a ortografia), então a saída do meu script perl A resposta para sua última pergunta pode ser canalizada para uniq -f2 para obter a saída desejada.

A opção -f2 informa uniq para ignorar os dois primeiros campos ao comparar as linhas para exclusividade.

por exemplo. com a linha @lines = map ... no script perl alterada para:

@lines = map { $_ =~ s/(source|destinati?ion)$/both/oi; $_} @lines;

A saída é assim:

$ ./swatesh.pl < swatesh2.txt | uniq -f2
2014-11-24 12:59:42.169 101.0.0.0 source
2014-11-24 12:59:40.375 104.156.80.0 destinatiion
2014-11-24 12:59:36.729 104.219.48.0 destinatiion
2014-11-24 12:59:40.377 104.37.160.0 source
2014-11-24 12:58:58.902 107.188.128.0 both
2014-11-24 12:59:06.840 107.192.0.0 both
2014-11-24 12:59:55.488 111.0.0.0 both
2014-11-24 12:59:33.209 108.175.32.0 both

Se você quiser a saída de data e hora mais recente , primeiro classifique a entrada na ordem inversa pelo endereço IP (campo 3) e depois pela data e hora (campos 1 e 2) antes de conectá-la no meu script perl (swatesh.pl) e uniq -f2 . Opcionalmente, classifique a saída final de modo que fique em ordem crescente de data e hora. por exemplo,

$ sort -r -k3,3 -k1,2 swatesh2.txt | ./swatesh.pl | uniq -f2 | sort     
2014-11-24 12:59:06.456 107.188.128.0 both
2014-11-24 12:59:33.209 108.175.32.0 both
2014-11-24 12:59:36.729 104.219.48.0 destinatiion
2014-11-24 12:59:40.375 104.156.80.0 destinatiion
2014-11-24 12:59:40.377 104.37.160.0 source
2014-11-24 12:59:42.043 107.192.0.0 both
2014-11-24 12:59:42.169 101.0.0.0 source
2014-11-24 12:59:55.488 111.0.0.0 both
    
por 08.06.2016 / 07:11
0

Eu modifiquei o código dado no OP:

awk '{print $3}' input.txt | sort -u | while read line
do 
    echo -n 'grep $line input.txt | \
      sort -r | head -1 | \
      grep -oe "[^a-z]*"' ' ' # print latest time stamp
    if [[ $(grep -c $line input.txt) -ge 2 ]];  then 
        echo  'both'
    else
        echo 'grep $line input.txt | grep -oe "[a-z]*"'
    fi
done

Saída:

2014-11-24 12:59:42.169 101.0.0.0  source
2014-11-24 12:59:40.375 104.156.80.0  destination
2014-11-24 12:59:36.729 104.219.48.0  destination
2014-11-24 12:59:40.377 104.37.160.0  source
2014-11-24 12:59:06.456 107.188.128.0  both
2014-11-24 12:59:42.043 107.192.0.0  both
2014-11-24 12:59:33.209 108.175.32.0  both
2014-11-24 12:59:55.488 111.0.0.0  both
    
por 09.06.2016 / 09:22
0

Eu fiz o script abaixo. Classifica o resultado por data.
Chame de fix.sh , invoque bash fix.sh <LOGFILENAME>

function fix {
    while read IP TYPE HOUR DATE; do
        if [ ! -z "$PREVIOUS_IP" ]; then
            if [ $IP == $PREVIOUS_IP ]; then
                TYPE=both
                [[ $DATE < $PREVIOUS_DATE ]] && DATE="$PREVIOUS_DATE"
                EQUAL_IPS=true
            else
                echo $PREVIOUS_LINE
                EQUAL_IPS=false
            fi
        fi
        PREVIOUS_DATE="$DATE"
        PREVIOUS_IP=$IP;
        PREVIOUS_LINE="$IP $TYPE $HOUR $DATE";
    done
    [ "$EQUAL_IPS" == true ] && echo $PREVIOUS_LINE
}

LOGFILE=$1

cat $LOGFILE | awk '{print $3 " " $4 " " $1 " " $2;}' | \
  sort | fix | awk '{print $3 " " $4 " " $1 " " $2;}' | sort

Sua saída é:

2014-11-24 12:59:06.456 107.188.128.0 both
2014-11-24 12:59:33.209 108.175.32.0 both
2014-11-24 12:59:36.729 104.219.48.0 destination
2014-11-24 12:59:40.375 104.156.80.0 destination
2014-11-24 12:59:40.377 104.37.160.0 source
2014-11-24 12:59:42.043 107.192.0.0 both
2014-11-24 12:59:42.169 101.0.0.0 source
2014-11-24 12:59:55.488 111.0.0.0 both
    
por 08.06.2016 / 18:10