Cuenta el número total de líneas antes / después de una coincidencia de patrón

9

Tengo una larga lista de direcciones IP, que no están en secuencia. Necesito encontrar cuántas direcciones IP hay antes / después de una dirección IP en particular. ¿Cómo puedo conseguir esto?

Mandar Shinde
fuente
¿Tienes IP duplicada?
cuonglm
No. Todas las direcciones IP son únicas.
Mandar Shinde
¿Qué significa antes / después para una dirección IP? En particular, ¿tiene direcciones IPv4 e IPv6? ¿Cómo se comparan?
vinc17
¿Necesita el archivo ordenado?
cuonglm
2
@ vinc17: el archivo solo contiene direcciones IP (IPv4), no se incluyen otros datos. Si hay 1000 direcciones IP en total y la coincidencia se encuentra en la ubicación número 300, significa que hay 299 líneas antes de la coincidencia y 700 líneas después de la coincidencia.
Mandar Shinde

Respuestas:

8

Número de líneas antes y después de una coincidencia, incluida la coincidencia (es decir, debe restar 1 del resultado si desea excluir la coincidencia):

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

Pero esto no tiene nada que ver con las direcciones IP en particular.

vinc17
fuente
4

Quizás lo más fácil es,

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

Gracias @JoshepR por señalar el error

jpmuc
fuente
Esto solo imprime el número de línea en el que ocurrió el patrón.
Joseph R.
@JosephR. - no, imprime cada número de línea en el que se produce cada coincidencia.
mikeserv
@mikeserv Lo sé, pero el OP especificó que las direcciones IP son únicas. El OP tampoco quiere el número de línea donde ocurrieron las coincidencias; quieren el número de líneas antes de que ocurra el patrón y el número de líneas después de él.
Joseph R.
@JosephR: la forma más rápida de llegar a esos recuentos es contar los números de línea dc; probablemente lo dirija directamente a mí mismo, probablemente.
mikeserv
@mikeserv No estoy argumentando que la información de esta respuesta no sea útil, solo digo que este código por sí solo no hace lo que el OP quiere.
Joseph R.
3

Lo hice de dos maneras, aunque creo que me gusta más:

: $(( 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

Eso los guarda a todos como variables de shell actuales, y luego los evalúa en el ciclo for para la salida. Cuenta el total de líneas en el archivo wcy obtiene el primer número de línea coincidente con sed.

Su salida:

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

Yo también hice:

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 solo los números coincidentes y de la última línea, luego trtraduce las \nlíneas electrónicas intermedias a, y readlee el primero de sedlos resultados en $mly todos los demás en $ll. Los posibles casos de coincidencias múltiples se manejan eliminando todos los resultados, excepto el último, de $llla expansión cuando se configura de nuevo más tarde.

Su salida:

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

Ambos métodos fueron probados en el archivo generado de la siguiente manera:

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 

Lo hace, por número de línea:

  1. establece la cadena de búsqueda
  2. realiza cinco bucles para asegurarse de que haya múltiples coincidencias
  3. imprime 199 ceros y "$IP"luego una línea \nelectrónica
  4. salida de tuberías a tr- que traduce ceros a \nlíneas de flujo y luego~/file
mikeserv
fuente
2

Aquí hay un poco de código Perl que lo hace:

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

Esto cuenta el número total de líneas antes y después de la línea que contiene la IP 192.168.1.1. Reemplace con su IP deseada.

Usando nada más que 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.
fuente
BASH es preferido.
Mandar Shinde
2
@Joseph R .: ¿Por qué no usas en $.lugar de un contador?
cuonglm
@Gnouc podría, por supuesto. Sólo creo que esto es más fácil de leer que el establecimiento $aftera $. - $before.
Joseph R.
No, quiero decir: si coincide, imprima $. - 1, guarde $.en $tmp. Fin de impresión $. - $tmp. Por lo tanto, no necesitamos un contador para ambos antes y después. Por supuesto, es menos legible que el tuyo.
cuonglm
@MandarShinde Por favor, vea la edición. Agregué una respuesta pura de Bash.
Joseph R.
2

Estaba probando los siguientes comandos, que son un poco complicados, pero darían resultados precisos:

Después:

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

Antes de:

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

Una awksolución que informa el número de líneas antes y después del último partido

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

Greptiene una función que puede contar la cantidad de veces que se encuentra un patrón en particular. Si usa el -ccomando, lo hará. Con el comando -cy -v, esto contará cuántas veces esto no coincide con un patrón en particular

Ejemplo:

grep -c -v <pattern> file

Entonces, si intentas algo como:

grep -c -v 192.168.x.x file.log eso debería funcionar.

ryekayo
fuente
Esto cuenta el número de ocurrencias de la IP de destino. Esto no es lo que solicitó el OP.
Joseph R.
Acabo de editarlo, si él está pidiendo contar todas las demás IP antes y después de una IP en particular, la edición debería funcionar para él.
ryekayo