Załóżmy, że masz plik zawierający adresy IP, jeden adres w każdej linii:
10.0.10.1
10.0.10.1
10.0.10.3
10.0.10.2
10.0.10.1
Potrzebujesz skryptu powłoki, który liczy dla każdego adresu IP, ile razy pojawia się w pliku. Do poprzedniego wejścia potrzebne są następujące dane wyjściowe:
10.0.10.1 3
10.0.10.2 1
10.0.10.3 1
Jednym ze sposobów na to jest:
cat ip_addresses |uniq |while read ip
do
echo -n $ip" "
grep -c $ip ip_addresses
done
Jednak naprawdę nie jest to wydajne.
Jak rozwiązalibyście ten problem bardziej efektywnie za pomocą bash?
(Jedną rzecz do dodania: wiem, że można to rozwiązać z Perla lub awk, interesuje mnie lepsze rozwiązanie w bash, a nie w tych językach).
DODATKOWE INFORMACJE:
Załóżmy, że plik źródłowy ma 5 GB, a komputer z algorytmem ma 4 GB. Sortowanie nie jest więc skutecznym rozwiązaniem, ani odczytywanie pliku więcej niż jeden raz.
Podobało mi się rozwiązanie przypominające hashtable - ktoś może ulepszyć to rozwiązanie?
INFORMACJE DODATKOWE # 2:
Niektórzy ludzie pytali, dlaczego miałbym zawracać sobie tym głowę, kiedy jest to o wiele łatwiejsze, np. W Perlu. Powodem jest to, że na maszynie musiałem zrobić ten perl nie był dla mnie dostępny. Była to specjalnie zbudowana maszyna linuksowa bez większości narzędzi, do których jestem przyzwyczajony. Myślę, że to był interesujący problem.
Więc proszę, nie obwiniaj tego pytania, po prostu zignoruj je, jeśli ci się nie podoba. :-)
Odpowiedzi:
Spowoduje to wydrukowanie liczby jako pierwszej, ale poza tym powinna być dokładnie taka, jak chcesz.
źródło
sort ip_addresses | uniq -c | sort -nr
sort ip_addresses | uniq -c | sort -nr | awk '{ print $2, $1 }'
aby uzyskać adres IP w pierwszej kolumnie i liczyć na sekundę.sort -nr -k1,1
Szybka i brudna metoda jest następująca:
cat ip_addresses | sort -n | uniq -c
Jeśli musisz użyć wartości w bash, możesz przypisać całe polecenie do zmiennej bash, a następnie przejrzeć wyniki.
PS
Jeśli polecenie sortowania zostanie pominięte, nie uzyskasz poprawnych wyników, ponieważ uniq patrzy tylko na kolejne identyczne linie.
źródło
do sumowania wielu pól na podstawie grupy istniejących pól skorzystaj z poniższego przykładu: (zamień 1 $, 2 $, 3 $, 4 $ zgodnie z własnymi wymaganiami)
źródło
sort
iuniq
są najłatwiejsze do zliczania, ale nie pomagają, gdy musisz obliczyć / sumować wartości pól. Składnia tablicy awk jest bardzo potężna i kluczem do grupowania tutaj. Dzięki!print
funkcja wydaje się w dół skali 64-bitowe liczby całkowite do 32 bitów, więc dla wartości int przekraczającej 2 ^ 31 może chcesz korzystaćprintf
z%.0f
formatu zamiastprint
tamarr[$1,$2]+=$3+$4
np.arr[$1,$2]=(arr[$1,$2] $3 "," $4). I needed this to provide a grouped-by-package list of files (two columns only) and used:
Arr [$ 1] = (arr [$ 1] $ 2) `z powodzeniem.Rozwiązaniem kanonicznym jest to, o którym wspomniał inny respondent:
Jest krótszy i bardziej zwięzły niż to, co można napisać w Perlu lub awk.
Piszesz, że nie chcesz używać sortowania, ponieważ rozmiar danych jest większy niż rozmiar głównej pamięci urządzenia. Nie lekceważ jakości implementacji polecenia sortowania w Uniksie. Sort był używany do obsługi bardzo dużych ilości danych (na przykład danych rozliczeniowych oryginalnych AT&T) na komputerach z 128k (czyli 131.072 bajtów) pamięci (PDP-11). Kiedy sort napotyka więcej danych niż ustalony limit (często dostosowany do wielkości głównej pamięci urządzenia), sortuje dane, które odczytał w pamięci głównej i zapisuje je w pliku tymczasowym. Następnie powtarza akcję z kolejnymi porcjami danych. Wreszcie wykonuje sortowanie scalające na tych plikach pośrednich. Umożliwia to sortowanie pracy na danych wiele razy większych niż pamięć główna maszyny.
źródło
to polecenie dałoby pożądany wynik
źródło
Wygląda na to, że musisz użyć dużej ilości kodu do symulacji skrótów w bashu, aby uzyskać zachowanie liniowe, lub trzymać się
kwadratowychwersji superlinearnych.Wśród tych wersji rozwiązanie saui jest najlepsze (i najprostsze):
Znalazłem http://unix.derkeiler.com/Newsgroups/comp.unix.shell/2005-11/0118.html . Ale to brzydkie jak diabli ...
źródło
Rozwiązanie (pogrupuj według mysql)
Wynik
źródło
Prawdopodobnie możesz użyć samego systemu plików jako tabeli skrótów. Pseudo-kod w następujący sposób:
Na koniec wystarczy przejrzeć wszystkie pliki i wydrukować w nich nazwy i numery plików. Alternatywnie, zamiast utrzymywać liczbę, możesz za każdym razem dodawać spację lub znak nowej linii do pliku, a na koniec spójrz na rozmiar pliku w bajtach.
źródło
Uważam, że tablica asocjacyjna awk jest również przydatna w tym przypadku
Grupa pocztą tutaj
źródło
Większość innych rozwiązań liczy duplikaty. Jeśli naprawdę musisz pogrupować pary klucz-wartość, spróbuj tego:
Oto moje przykładowe dane:
Spowoduje to wydrukowanie par wartości klucza pogrupowanych według sumy kontrolnej md5.
źródło
Czysty grzmotnąć (bez widelca!)
Jest sposób, używając grzmotnąćfunkcja . Ta droga jest bardzo szybka, ponieważ nie ma widelca! ...
... Podczas gdy paczka adresów IP pozostaje niewielka !
Uwaga: Adresy IP są konwertowane na 32-bitową liczbę całkowitą bez znaku, używaną jako indeks tablicy . Używaj prostych tablic bash , a nie tablic asocjacyjnych (co jest droższe)!
Na moim hoście jest to o wiele szybsze niż używanie forksów, do około 1 000 adresów, ale zajmie około 1 całej sekundy, kiedy spróbuję posortować i policzyć 10 000 adresów.
źródło
Zrobiłbym to w ten sposób:
ale uniq może dla ciebie pracować.
źródło
Rozumiem, że szukasz czegoś w Bash, ale na wypadek, gdyby ktoś inny szukał czegoś w Pythonie, możesz rozważyć:
Ponieważ wartości w zestawie są domyślnie unikalne, a Python jest w tym całkiem niezły, możesz tutaj coś wygrać. Nie testowałem kodu, więc może być uszkodzony, ale może Cię tam doprowadzić. A jeśli chcesz policzyć zdarzenia, użycie dykta zamiast zestawu jest łatwe do wdrożenia.
Edycja: Jestem kiepskim czytelnikiem, więc odpowiedziałem źle. Oto fragment kodu ze słowem uwzględniającym zdarzenia.
Słownik mydict przechowuje teraz listę unikatowych adresów IP jako kluczy i liczbę ich wystąpień jako wartości.
źródło
itertools.groupby()
co w połączeniu zsorted()
robi dokładnie to, o co prosi OP.Sortowanie można pominąć, jeśli kolejność nie jest znacząca
lub
jeśli lista źródeł jest zmienną
źródło