0Pricing
Linux Command Line & Bash Scripting Mastery · Lekcja

Agregowanie za pomocą tablic i grupowania w awk

Obliczaj sumy, liczności i podsumowania według grup, używając tablic asocjacyjnych indeksowanych wartościami pól.

Agregowanie za pomocą tablic i grupowania w awk to bezpłatna lekcja Linux Command Line & Bash Scripting Mastery na CoddyKit. To lekcja 3 z 4. Możesz przeczytać całą lekcję poniżej za darmo — a potem ćwiczyć ją interaktywnie w przeglądarce z wbudowanym edytorem kodu i tutorem AI dostępnym 24/7. To część ścieżki edukacyjnej Linux Command Line & Bash Scripting Mastery, a Twój postęp synchronizuje się między webem a aplikacją CoddyKit. Kurs Linux Command Line & Bash Scripting Mastery zawiera 4 lekcji w sumie.

Czym są tablice asocjacyjne awk

W awk tablica asocjacyjna to struktura przechowująca pary klucz-wartość, w której kluczami mogą być dowolne ciągi znaków lub liczby. W przeciwieństwie do tablic indeksowanych używanych w większości języków, tablice awk są wewnętrznie mapami haszującymi — doskonale nadają się do grupowania i agregowania danych według wartości pól.

  • Deklarowanie niejawne: wystarczy przypisać arr[key] = value
  • Klucze są domyślnie ciągami znaków (liczby są konwertowane)
  • Brak limitu rozmiaru — awk powiększa tablicę w miarę potrzeb
  • Idealne do obliczania sum, liczności i podsumowań według grup w jednym przejściu

Nie trzeba inicjalizować klucza przed jego zwiększeniem — awk automatycznie traktuje nieustawiony klucz jako zero lub pusty ciąg znaków.

Zliczanie wierszy w poszczególnych grupach

Najczęściej stosowany wzorzec agregacji polega na zliczaniu, ile razy każda unikatowa wartość pola występuje w danych. Użyj pola $1 (lub dowolnego innego pola) jako klucza tablicy i zwiększaj licznik przy każdym pasującym wierszu.

Blok END jest wykonywany po odczytaniu całego wejścia — to w nim należy wypisać zgromadzone wyniki.

count[$1]++

Po przetworzeniu tablica count zawiera całkowitą liczbę wierszy dla każdej unikatowej wartości $1.

#!/usr/bin/env bash
# Count how many log entries exist per HTTP status code
# Input format: <ip> <date> <method> <path> <status> <bytes>
printf '10.0.0.1 2024-01-01 GET /index 200 512
10.0.0.2 2024-01-01 POST /api 404 128
10.0.0.3 2024-01-01 GET /img 200 2048
10.0.0.4 2024-01-01 GET /api 500 64
10.0.0.5 2024-01-01 DELETE /api 404 32
' | awk '
{
    count[$5]++
}
END {
    for (status in count)
        print status, count[status]
}'

Sumowanie pola liczbowego w poszczególnych grupach

Zliczanie to tylko jedna z form agregacji. Aby sumować pole liczbowe pogrupowane według innego pola, dodawaj wartość liczbową do tablicy, której kluczem jest pole grupowania.

Wzorzec wygląda następująco:

  • Klucz = pole używane do grupowania (np. $1 jako nazwa użytkownika)
  • Wartość = bieżąca suma pola liczbowego (np. $2 jako liczba bajtów)

W jednym przejściu awk obliczana jest pełna suma dla każdej grupy — sortowanie ani wstępne przetwarzanie nie są wymagane.

#!/usr/bin/env bash
# Sum bytes transferred per user from an access log
printf 'alice 1024
bob 512
alice 2048
charlie 256
bob 768
alice 128
' | awk '
{
    bytes[$1] += $2
}
END {
    for (user in bytes)
        printf "%-10s %d bytes\n", user, bytes[user]
}'

Łączenie zliczania i sumowania w jednym przejściu

awk pozwala jednocześnie utrzymywać wiele tablic, dzięki czemu w jednym przebiegu pliku można obliczyć dla każdej grupy liczność i sumę, a tym samym także średnią. Jest to znacznie wydajniejsze niż dwukrotne uruchamianie potoku.

  • count[key]++ — śledzi liczbę wystąpień
  • total[key] += $N — śledzi bieżącą sumę
  • W END podziel total[k] / count[k], aby obliczyć średnią dla danej grupy
#!/usr/bin/env bash
# Compute per-department headcount and average salary
printf 'Engineering Alice 95000
Engineering Bob 88000
Marketing Carol 72000
Marketing Dave 68000
Engineering Eve 102000
Marketing Frank 75000
' | awk '
{
    dept  = $1
    sal   = $3
    count[dept]++
    total[dept] += sal
}
END {
    printf "%-15s %5s %10s\n", "Department", "Count", "Avg Salary"
    for (d in count)
        printf "%-15s %5d %10.0f\n", d, count[d], total[d]/count[d]
}'

Klucze wielopolowe do grupowania 2D

Możesz utworzyć klucz złożony, łącząc wiele pól separatorem. Pozwala to przeprowadzić dwuwymiarowe grupowanie bez używania specjalnej składni.

Wybierz separator, który nie może wystąpić w danych (np. SUBSEP, wbudowany separator rekordów awk \034 lub dosłowny znak potoku |).

  • Klucz złożony: arr[$1 SUBSEP $2] lub arr[$1"|"$2]
  • Rozdzielenie klucza podczas wypisywania: split(key, parts, "|")
#!/usr/bin/env bash
# Count requests grouped by HTTP method AND status code
printf 'GET 200
POST 201
GET 404
GET 200
DELETE 204
POST 201
GET 404
POST 500
' | awk '
{
    key = $1 "|" $2
    count[key]++
}
END {
    printf "%-8s %-6s %s\n", "Method", "Status", "Count"
    for (k in count) {
        split(k, parts, "|")
        printf "%-8s %-6s %d\n", parts[1], parts[2], count[k]
    }
}'

Śledzenie wartości minimalnej i maksymalnej w poszczególnych grupach

Tablice asocjacyjne ułatwiają śledzenie wartości min i max dla poszczególnych grup. Sztuczka polega na inicjalizacji tylko przy pierwszym wystąpieniu każdego klucza za pomocą specjalnego warunku !(key in arr).

  • !(key in min_arr) ma wartość true przy pierwszym napotkaniu klucza
  • Po inicjalizacji porównuj wartości i zastępuj je za pomocą standardowego sprawdzenia mniejszości / większości

Ten wzorzec zapobiega pojawieniu się pozornego zera, które zafałszowałoby minimum.

#!/usr/bin/env bash
# Find min and max response time per endpoint
printf '/api/users 120
/api/orders 340
/api/users 98
/api/orders 512
/api/users 210
/api/orders 280
/health 5
/health 7
' | awk '
{
    ep  = $1
    rt  = $2
    if (!(ep in mn)) { mn[ep] = rt; mx[ep] = rt }
    if (rt < mn[ep]) mn[ep] = rt
    if (rt > mx[ep]) mx[ep] = rt
}
END {
    printf "%-15s %6s %6s\n", "Endpoint", "Min", "Max"
    for (ep in mn)
        printf "%-15s %6d %6d\n", ep, mn[ep], mx[ep]
}'

Rozkład częstości — grupy Top-N

Częstym zadaniem w praktyce jest znalezienie Top-N najczęściej występujących wartości. awk zajmuje się zliczaniem, a potoki sort i head służą do uszeregowania i ograniczenia wyników.

Ten wzorzec potoku jest idiomatyczny w pracy z powłoką:

  1. awk zlicza wystąpienia w tablicy, a w END wypisuje count key
  2. sort -rn sortuje liczbowo malejąco
  3. head -n 5 pozostawia tylko 5 najwyższych wyników

Skupienie awk na agregacji i przekazanie sortowania do sort jest zgodne z filozofią Uniksa.

#!/usr/bin/env bash
# Top 3 most active IP addresses from an access log
printf '192.168.1.10 GET /index
10.0.0.5 POST /api
192.168.1.10 GET /css
172.16.0.3 GET /index
10.0.0.5 GET /api
192.168.1.10 DELETE /api
172.16.0.3 POST /login
10.0.0.5 GET /index
10.0.0.5 POST /logout
' | awk '{count[$1]++} END {for (ip in count) print count[ip], ip}' \
  | sort -rn \
  | head -n 3

Używanie NR i FNR do agregacji z wielu plików

Podczas przetwarzania wielu plików wbudowane zmienne awk NR (łączna liczba odczytanych rekordów) i FNR (liczba rekordów w bieżącym pliku) pozwalają, za pomocą FILENAME, oznaczyć plik, z którego pochodzi każdy rekord.

  • FILENAME — nazwa aktualnie odczytywanego pliku
  • FNR == 1 — jest spełnione na początku każdego nowego pliku (przydatne do pomijania nagłówków)

Pozwala to przeprowadzać agregację według grup dla poszczególnych plików w całym katalogu dzienników za pomocą jednego wywołania awk.

Wypisywanie posortowanych wyników z tablic awk

Pętla for (key in array) w awk nie gwarantuje kolejności. Aby uzyskać deterministyczne wyniki, przekaż dane wypisywane w bloku END do sort albo zbierz wartości i posortuj je w awk za pomocą funkcji asorti / asort (tylko GNU awk).

Przenośne podejście oparte na potoku powłoki jest zwykle preferowane w skryptach wieloplatformowych:

  • sort -k1,1 — sortowanie alfabetyczne według pierwszego pola (klucza grupy)
  • sort -k2,2rn — sortowanie według drugiego pola (liczności/sumy), liczbowo malejąco
#!/usr/bin/env bash
# Bytes per user, sorted alphabetically by username
printf 'zara 800
alice 1500
bob 300
alice 200
zara 400
bob 1100
' | awk '
{
    total[$1] += $2
}
END {
    for (u in total)
        print u, total[u]
}' | sort -k1,1

Usuwanie kluczy tablicy i czyszczenie stanu

Czasami trzeba zresetować stan agregacji w trakcie przetwarzania — na przykład podczas przetwarzania plików dzienników, w których poszczególne sekcje są oddzielone pustym wierszem lub wartością sygnalizującą.

  • delete arr[key] — usuwa pojedynczy wpis
  • delete arr — czyści całą tablicę (GNU awk)
  • Sprawdzaj istnienie za pomocą (key in arr) przed uzyskaniem dostępu, aby uniknąć tworzenia pozornych wpisów

Technika ta umożliwia agregację w przesuwanym oknie lub według sekcji bez ponownego uruchamiania awk.

#!/usr/bin/env bash
# Sum values within each section delimited by "---"
printf 'alice 100
bob 200
---
alice 50
bob 75
charlie 300
---
' | awk '
/^---/ {
    print "-- Section totals --"
    for (u in total) printf "  %-10s %d\n", u, total[u]
    delete total
    next
}
{
    total[$1] += $2
}'

Potok z praktyki: podsumowanie dzienników Nginx

Połączmy wszystkie elementy — oto gotowa do użycia produkcyjnie, jednoprzebiegowa agregacja awk dla formatu combined log serwera Nginx. W jednym przebiegu oblicza liczbę żądań, łączną liczbę bajtów i współczynnik błędów dla każdego wirtualnego hosta.

Wykorzystane techniki:

  • Klucz złożony: vhost wyodrębniony z pola
  • Równoległe tablice: reqs[], bytes[], errors[]
  • Warunkowe gromadzenie danych: $9 >= 400 do zliczania tylko odpowiedzi błędnych
  • Sformatowana tabela printf w bloku END
#!/usr/bin/env bash
# Simulated Nginx combined log: host ip - - [date] "METHOD /path HTTP/1.1" status bytes
printf 'api.example.com 10.0.0.1 - - [01/Jan/2024] "GET /v1" 200 1024
www.example.com 10.0.0.2 - - [01/Jan/2024] "GET /" 200 4096
api.example.com 10.0.0.3 - - [01/Jan/2024] "POST /v1" 500 128
www.example.com 10.0.0.4 - - [01/Jan/2024] "GET /img" 404 64
api.example.com 10.0.0.5 - - [01/Jan/2024] "GET /v1" 200 2048
www.example.com 10.0.0.6 - - [01/Jan/2024] "GET /" 200 4096
api.example.com 10.0.0.7 - - [01/Jan/2024] "DELETE /v1" 403 32
' | awk '
{
    host = $1; status = $9; b = $10
    reqs[host]++
    bytes[host] += b
    if (status + 0 >= 400) errors[host]++
}
END {
    printf "%-20s %6s %10s %6s\n", "Host", "Reqs", "Bytes", "Errors"
    for (h in reqs)
        printf "%-20s %6d %10d %6d\n", h, reqs[h], bytes[h], errors[h]+0
}'

Sprawdzenie wiedzy: poprawna inicjalizacja minimum

Sprawdź, czy rozumiesz częsty problem podczas agregacji w awk.

Podsumowanie lekcji: agregacja za pomocą tablic awk

Poznałeś podstawowe wzorce obliczania agregacji według grup w całości za pomocą awk:

  • Zliczanie: count[$key]++ — zwiększaj przy każdym wierszu, wypisz w END
  • Sumowanie: total[$key] += $N — gromadź wartości liczbowe dla poszczególnych grup
  • Średnia: utrzymuj zarówno count[], jak i total[], a następnie dziel w END
  • Min/Max: zainicjalizuj przy pierwszym wystąpieniu za pomocą !(key in arr), a następnie porównuj
  • Klucze złożone: łącz pola separatorem, aby grupować według wielu wymiarów
  • Posortowane wyniki: przekaż dane wypisywane przez awk w END do sort, aby uzyskać deterministyczną kolejność
  • Resetowanie sekcji: użyj delete arr, aby wyczyścić stan między logicznymi sekcjami wejścia

Wzorce te pozwalają zastąpić rozbudowane zapytania do baz danych lub wielokrotne przejścia potoku jednym wydajnym wywołaniem awk — to niezbędna umiejętność w produkcyjnej pracy z powłoką.

Często zadawane pytania

Czy lekcja „Agregowanie za pomocą tablic i grupowania w awk” jest bezpłatna?

Tak — pełny tekst „Agregowanie za pomocą tablic i grupowania w awk” jest dostępny za darmo tutaj w sieci. Aby ćwiczyć ją interaktywnie (wbudowany edytor kodu i tutor AI dostępny 24/7) i odblokować resztę kursu Linux Command Line & Bash Scripting Mastery, przejdź na CoddyKit PRO. Kurs Linux Command Line & Bash Scripting Mastery zawiera 4 lekcji w sumie.

Co nauczysz się w „Agregowanie za pomocą tablic i grupowania w awk”?

Obliczaj sumy, liczności i podsumowania według grup, używając tablic asocjacyjnych indeksowanych wartościami pól. Ćwiczysz Linux Command Line & Bash Scripting Mastery z praktycznym kodem, który uruchamiasz bezpośrednio w przeglądarce, a tutor AI dostępny 24/7 odpowiada na Twoje pytania podczas pracy nad lekcją.

Czy potrzebuję doświadczenia, aby zacząć Linux Command Line & Bash Scripting Mastery?

Nie wymagamy żadnego doświadczenia. Linux Command Line & Bash Scripting Mastery w CoddyKit jest strukturyzowany dla początkujących i zaawansowanych użytkowników, więc możesz zacząć tutaj lub od początku i uczyć się w swoim tempie. To lekcja 3 z 4.

Ile czasu zajmuje lekcja „Agregowanie za pomocą tablic i grupowania w awk”?

Większość lekcji CoddyKit trwa około 5–10 minut. Każda lekcja to mały, interaktywny krok, dzięki czemu robisz systematyczne postępy i zawsze wracasz dokładnie do tego samego miejsca — na webie i w aplikacji.

Czy mogę pisać i uruchamiać kod w tej lekcji Linux Command Line & Bash Scripting Mastery?

Tak. Każda lekcja Linux Command Line & Bash Scripting Mastery zawiera wbudowany edytor kodu, więc piszesz i uruchamiasz prawdziwy kod bezpośrednio w przeglądarce i od razu otrzymujesz sprzężenie zwrotne od AI — bez konfiguracji na komputerze.

Wszystkie lekcje w tym kursie

  1. Rekordy, pola i niestandardowe separatory w awk
  2. Wzorce, zakresy oraz bloki BEGIN/END
  3. Agregowanie za pomocą tablic i grupowania w awk
  4. Funkcje awk, formatowanie printf i generowanie raportów
← Powrót do Linux Command Line & Bash Scripting Mastery