Policz całkowitą liczbę linii przed / po dopasowaniu wzorca

9

Mam długą listę adresów IP, które nie są w kolejności. Muszę znaleźć liczbę adresów IP przed / po określonym adresie IP. Jak mogę to osiągnąć?

Mandar Shinde
źródło
Czy masz zduplikowane IP?
cuonglm
Nie. Wszystkie adresy IP są unikalne.
Mandar Shinde
Co oznacza „przed / po” dla adresu IP? W szczególności, czy masz zarówno adresy IPv4, jak i IPv6? Jak oni się porównują?
vinc17
Potrzebujesz posortowanego pliku?
cuonglm
2
@ vinc17 - plik zawiera tylko adresy IP (IPv4), nie zawiera innych danych. Jeśli w sumie jest 1000 adresów IP, a dopasowanie zostanie znalezione w 300. lokalizacji, oznacza to, że jest 299 linii przed meczem i 700 linii po meczu.
Mandar Shinde

Odpowiedzi:

8

Liczba linii przed i po meczu, w tym dopasowanie (tzn. Musisz odjąć 1 od wyniku, jeśli chcesz wykluczyć dopasowanie):

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

Nie ma to jednak żadnego związku z adresami IP.

vinc17
źródło
4

Może najłatwiej jest

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

Dzięki @JoshepR za wskazanie błędu

jpmuc
źródło
To po prostu drukuje numer linii, na której wystąpił wzór.
Joseph R.
@JosephR. - nie, wypisuje każdy numer linii, na której występuje każde dopasowanie.
mikeserv
@ Mikeserv Wiem, ale OP określił, że adresy IP są unikalne. OP nie chce również numeru linii, w której wystąpiły dopasowania; chcą liczby linii przed wystąpieniem wzoru i liczby linii po nim.
Joseph R.
@JosephR - najszybszym sposobem, aby dojść do tych liczb, jest zsumowanie numerów linii - prawdopodobnie po prostu skierowałbym to bezpośrednio do dcsiebie.
mikeserv
@ mikeserv Nie twierdzę, że informacje z tej odpowiedzi nie są przydatne, po prostu mówię, że ten kod sam w sobie nie robi tego, czego chce OP.
Joseph R.
3

Zrobiłem to na dwa sposoby, ale myślę, że najbardziej to lubię:

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

To zapisuje wszystkie te zmienne jako bieżące zmienne powłoki - i następnie ocenia je w pętli for w celu uzyskania wyniku. Liczy całkowitą liczbę linii w pliku wci pobiera pierwszą dopasowaną liczbę linii za pomocą sed.

Jego wydajność:

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

Zrobiłem również:

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))
}

sedwypisuje tylko pasujące i ostatnie numery linii, a następnie trtłumaczy pośrednie \newline nai readwczytuje pierwszy z sedwyników do, $mla wszystkie pozostałe do $ll. Możliwe przypadki wielokrotnych dopasowań są obsługiwane przez usunięcie wszystkich wyników poza $llrozszerzeniem z rozwinięcia przy ustawianiu go ponownie później.

Jego wydajność:

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

Obie metody zostały przetestowane na pliku wygenerowanym w następujący sposób:

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 

Według numeru wiersza:

  1. ustawia szukany ciąg
  2. Pętle pięć razy, aby zapewnić, że będzie wiele dopasowań
  3. wypisuje 199 zer, a "$IP"następnie \newline
  4. wyprowadza potoki do tr- co przekłada zera na \newline na~/file
mikeserv
źródło
2

Oto trochę kodu Perla, który to robi:

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

Zlicza całkowitą liczbę linii przed i po linii zawierającej adres IP 192.168.1.1. Zastąp żądanym adresem IP.

Używanie tylko 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.
źródło
BASH jest preferowana.
Mandar Shinde
2
@Joseph R .: Dlaczego nie użyjesz $.zamiast licznika?
cuonglm
@Gnouc oczywiście. Myślę, że jest to bardziej czytelne niż ustawienie $afterna $. - $before.
Joseph R.
Nie, mam na myśli: jeśli są dopasowane, wydrukuj $. - 1, zapisz $.w $tmp. Koniec wydruku $. - $tmp. Więc nie potrzebujemy licznika zarówno przed jak i po. Oczywiście jest mniej czytelny niż twój.
cuonglm
@MandarShinde Zobacz edycję. Dodałem czystą odpowiedź Basha.
Joseph R.
2

Próbowałem następujących poleceń, które są nieco skomplikowane, ale dałyby dokładne wyniki:

Po:

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

Przed:

echo "`cat -n file | grep <Pattern> | awk '{print $1}'`-1" | bc -l
Mandar Shinde
źródło
2

awkRaportowania liczby linii przed i po ostatnim meczu rozwiązanie

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

Grepma funkcję, która może zliczyć liczbę znalezienia określonego wzorca. Jeśli użyjesz -cpolecenia, które to zrobi. Za pomocą polecenia -ci -vbędzie to liczyć, ile razy nie pasuje to do określonego wzorca

Przykład:

grep -c -v <pattern> file

Więc jeśli spróbujesz czegoś takiego:

grep -c -v 192.168.x.x file.log to powinno działać.

ryekayo
źródło
Zlicza to liczbę wystąpień docelowego adresu IP. Nie o to prosił PO.
Joseph R.
Właśnie go edytowałem, jeśli poprosi o policzenie wszystkich innych adresów IP przed i po określonym adresie IP, edycja powinna dla niego działać.
ryekayo