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ąć?
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ąć?
Odpowiedzi:
Może najłatwiej jest
sed -n '/pattern/{=; q;}' file
Dzięki @JoshepR za wskazanie błędu
dc
siebie.
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 wc
i 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))
}
sed
wypisuje tylko pasujące i ostatnie numery linii, a następnie tr
tłumaczy pośrednie \n
ewline nai read
wczytuje pierwszy z sed
wyników do, $ml
a wszystkie pozostałe do $ll
. Możliwe przypadki wielokrotnych dopasowań są obsługiwane przez usunięcie wszystkich wyników poza $ll
rozszerzeniem 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:
"$IP"
następnie \n
ewlinetr
- co przekłada zera na \n
ewline na~/file
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"
$.
zamiast licznika?
$after
na $. - $before
.
$. - 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.
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
Grep
ma funkcję, która może zliczyć liczbę znalezienia określonego wzorca. Jeśli użyjesz -c
polecenia, które to zrobi. Za pomocą polecenia -c
i -v
bę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ć.