Contar o número total de linhas antes / depois de uma correspondência de padrão

9

Estou com uma longa lista de endereços IP, que não estão em sequência. Preciso descobrir quantos endereços IP existem antes / depois de um endereço IP específico. Como posso conseguir isso?

Mandar Shinde
fonte
Você tem IP duplicado?
cuonglm
Não. Todos os endereços IP são únicos.
22414 Mandar Shinde
O que significa antes / depois para um endereço IP? Em particular, você tem endereços IPv4 e IPv6? Como eles se comparam?
vinc17
Você precisa classificar o arquivo?
cuonglm
2
@ vinc17 - O arquivo contém apenas endereços IP (IPv4), nenhum outro dado é incluído. Se houver 1000 endereços IP no total, e a correspondência for encontrada no 300º local, significa que existem 299 linhas antes da correspondência e 700 linhas após a correspondência.
22414 Mandar Shinde

Respostas:

8

Número de linhas antes e depois de uma partida, incluindo a partida (ou seja, você precisa subtrair 1 do resultado se quiser excluir a partida):

sed -n '0,/pattern/p' file | wc -l
sed -n '/pattern/,$p' file | wc -l

Mas isso não tem nada a ver com endereços IP em particular.

vinc17
fonte
4

Talvez o mais fácil seja,

sed -n '/pattern/{=; q;}' file

Obrigado @JoshepR por apontar o erro

jpmuc
fonte
Isso apenas imprime o número da linha na qual o padrão ocorreu.
Joseph R.
@JosephR. - não, imprime todos os números de linha em que todas as correspondências ocorrem.
mikeserv
@mikeserv Eu sei, mas o OP especificou que os endereços IP são únicos. O OP também não deseja o número da linha onde as correspondências ocorreram; eles querem o número de linhas antes do padrão ocorrer e o número de linhas depois dele.
Joseph R.
@ JosephphR - a maneira mais rápida de chegar a essas contagens é contabilizar os números das linhas - eu direcionaria isso diretamente diretamente para dcmim, provavelmente.
mikeserv
@mikeserv Não estou argumentando que as informações desta resposta não são úteis, apenas estou dizendo que esse código por si só não faz o que o OP deseja.
Joseph R.
3

Fiz isso de duas maneiras, apesar de achar que gosto mais disso:

: $(( afterl=( lastl=$(wc -l <~/file) ) - 2 -
  $(( beforel=( matchl=$(sed -n "/$IP/{=;q;}" <~/file) ) - 1
)) ))
for n in last match afters befores
do  printf '%s line%s :\t%d\n' \
        "${n%s}" "${n##*[!s]}" $((${n%s}l))
done

Isso salva todas elas como variáveis ​​atuais do shell - e as avalia no loop for posteriormente para a saída. Ele conta o total de linhas no arquivo com wce obtém o primeiro número de linha correspondente sed.

Sua saída:

last line :     1000
match line :    200
after lines :   799
before lines :  199

Eu também fiz:

sed -n "/$IP/=;\$=" ~/file |  
tr \\n \  | { 
IFS=' ' read ml ll 
printf '%s line%s:\t%d\n' \
    last '' $((ll=${ll##* }))
    match '' $ml \
    after s "$((al=ll-ml-1)) \ 
    before s $((bl=ml-1))
}

sedimprime apenas números correspondentes e da última linha e, em seguida, trconverte as \nlinhas eletrônicas intermediárias eme readlê o primeiro dos sedresultados de $mle todos os outros $ll. Possíveis casos de correspondência múltipla são tratados removendo todos os resultados, exceto o último, da $llexpansão ao configurá-los novamente mais tarde.

Sua saída:

last line :     1000
match line :    200
after lines :   799
before lines :  199

Ambos os métodos foram testados no arquivo gerado da seguinte maneira:

IP='some string for which I seek' 
for count in 1 2 3 4 5 
do  printf '%.199d%s\n' 0 "$IP" 
done | tr 0 \\n >~/file 

Sim, pelo número da linha:

  1. define a sequência de pesquisa
  2. loops cinco vezes para garantir que haverá várias correspondências
  3. imprime 199 zeros e "$IP"depois uma linha de \new
  4. saída de tubos para tr- que traduz zeros em \nlinhas de e-mail e depois em~/file
mikeserv
fonte
2

Aqui está um pouco do código Perl que faz isso:

perl -ne '
     if(1 .. /192\.168\.1\.1/) { $before++ }
     else                      { $after++  }
     $before--; # The matching line was counted
     END{print "Before: $before, After: $after\n"}' your_file

Isso conta o número total de linhas antes e depois da linha que contém o IP 192.168.1.1. Substitua pelo seu IP desejado.

Usando nada além do Bash:

before=0
match=0
after=0
while read line;do
    if [ "$line" = 192.168.1.1 ];then
        match=1
    elif [ $match -eq 0 ];then
        before=$(($before+1))
    else
        after=$(($after + 1))
    fi
done < your_file
printf "Before: %d, After: %d\n" "$before" "$after"
Joseph R.
fonte
BASH é o preferido.
precisa
2
@ Joseph R .: Por que você não usa em $.vez de um contador?
cuonglm
@ Gnouc eu poderia, é claro. Eu só acho que isso é mais legível do que a criação $afterde $. - $before.
Joseph R.
Não, quero dizer: se corresponder, imprima $. - 1, salve $.em $tmp. Finalize a impressão $. - $tmp. Portanto, não precisamos de contador para antes e depois. Claro que é menos legível que o seu.
18714 cu cullm
@MandarShinde Por favor, veja a edição. Eu adicionei uma resposta pura do Bash.
Joseph R.
2

Eu estava tentando os seguintes comandos, que são um pouco complicados, mas dariam resultados precisos:

Depois de:

a=$(cat file | wc -l) && b=$(cat -n file | grep <Pattern> | awk '{print $1}') && echo "$a - $b" | bc -l

Antes:

echo "`cat -n file | grep <Pattern> | awk '{print $1}'`-1" | bc -l
Mandar Shinde
fonte
2

Uma awksolução que informa o número de linhas antes e depois da última partida

awk '/192\.168\.1\.1/{x=NR};{y=NR} END{printf "before-%d, after-%d\n" , x-1, y-x}'  file
iruvar
fonte
1

Greppossui um recurso que pode contar o número de vezes que um padrão específico é encontrado. Se você usar o -ccomando que fará isso. Com o comando -ce -v, isso contará quantas vezes isso não corresponde a um padrão específico

Exemplo:

grep -c -v <pattern> file

Então, se você tentar algo como:

grep -c -v 192.168.x.x file.log isso deve funcionar.

ryekayo
fonte
Isso conta o número de ocorrências do IP de destino. Não foi isso que o OP pediu.
Joseph R.
Acabei de editar, se ele está pedindo para contar todos os outros IPs antes e depois de um IP específico, a edição deve funcionar para ele.
ryekayo