Polecenie awk: wypisywanie pól, separator -F i wzorce warunkowe
Polecenie awk to linuksowe narzędzie, które czyta plik tekstowy wiersz po wierszu, dzieli każdy wiersz na pola według spacji lub wskazanego separatora i wybiera tylko pola z wierszy spełniających warunek, aby je wypisać lub policzyć. Na serwerze AlmaLinux 9 zliczono żądania według adresu IP i liczbę wpisów według kodu stanu w przykładowym pliku z logiem dostępu serwera WWW jednym wierszem połączonym potokiem z sort i okazało się, że wszystkie trzy odpowiedzi 404 z dwunastu wierszy to żądania wp-login.php i .env z jednego adresu IP.
Poniższe wyniki uzyskano na koncie root w folderze roboczym serwera AlmaLinux 9.8. Log dostępu, lista kont, zapisy sprzedaży i plik wyników to przykładowe pliki przygotowane do testu, a zawarte w nich adresy IP i nazwy kont również są wartościami przykładowymi.
1️⃣ Podstawowe użycie polecenia awk
awk dzieli każdy odczytany wiersz według spacji i numeruje pola od początku jako $1, $2, $3. Sprawdzono to na pliku scores.txt, w którym imię, wynik i punkty są rozdzielone spacjami.
cat scores.txtalice pass 82
bob fail 47
carol pass 91
dave pass 68
erin fail 55Nawiasy klamrowe w apostrofach zawierają akcję wykonywaną dla każdego wiersza, a jeśli po print wymienisz numery pól po przecinku, pojawią się tylko te pola. Po wypisaniu pierwszego i trzeciego pola zostają tylko imię i punkty.
awk '{print $1, $3}' scores.txtalice 82
bob 47
carol 91
dave 68
erin 55Jeśli usuniesz przecinek między polami i napiszesz $1 $3, obie wartości połączą się bez spacji. Przecinek w print wstawia separator wyjściowy (domyślnie jedna spacja), a wartości zapisane obok siebie bez przecinka łączą się w jeden ciąg znaków.
awk '{print $1 $3}' scores.txtalice82
bob47
carol91
dave68
erin55NR to numer dotąd odczytanego wiersza, a NF to liczba pól w tym wierszu. $NF oznacza ostatnie pole, więc wybierzesz ostatnią kolumnę nawet wtedy, gdy liczba pól zmienia się między wierszami.
awk '{print NR, NF, $NF}' scores.txt1 3 82
2 3 47
3 3 91
4 3 68
5 3 55Log dostępu serwera WWW też jest rozdzielony spacjami, więc pola wybierasz tak samo. Numery pól pierwszego wiersza testowego logu wyglądają tak.
| Pole | Wartość w pierwszym wierszu | Znaczenie |
|---|---|---|
| $1 | 192.0.2.10 | IP klienta |
| $4 | [29/Oct/2026:00:01:12 | Data i godzina żądania |
| $6 | "GET | Metoda żądania (z cudzysłowem na początku) |
| $7 | /index.html | Adres URL żądania |
| $9 | 200 | Kod stanu |
| $10 | 5120 | Wysłane bajty |
| $11 | "-" | Poprzednia strona (Referer) |
| $12~$15 | "Mozilla/5.0 (X11; Linux x86_64)" | User agent (dzielony przy każdej spacji) |
Wypisano $1, $7 i $9, czyli IP klienta, adres URL żądania i kod stanu.
awk '{print $1, $7, $9}' access.log192.0.2.10 /index.html 200
198.51.100.7 /about.html 200
192.0.2.10 /images/logo.png 200
203.0.113.25 /wp-login.php 404
203.0.113.25 /wp-login.php 404
192.0.2.10 /contact.html 200
198.51.100.7 /old-page.html 301
203.0.113.25 /.env 404
192.0.2.44 /index.html 200
198.51.100.7 /api/search?q=awk 500
192.0.2.44 /images/logo.png 304
192.0.2.10 /index.html 200Kolumna ze spacjami w wartości, taka jak user agent, dzieli się na kilka pól. NF we wszystkich trzech pierwszych wierszach wyniosło 15, a $NF to ostatni fragment user agenta.
awk '{print NF, $NF}' access.log | head -315 x86_64)"
15 x86_64)"
15 x86_64)"2️⃣ Ustawianie separatora przez awk -F
W pliku, którego kolumny rozdziela inny znak niż spacja, wpisz ten znak po -F. Po wypisaniu $1 bez -F z listy kont users.txt, przygotowanej w tej samej postaci co /etc/passwd, większość wierszy pojawiła się w całości.
awk '{print $1}' users.txtroot:x:0:0:root:/root:/bin/bash
bin:x:1:1:bin:/bin:/sbin/nologin
daemon:x:2:2:daemon:/sbin:/sbin/nologin
nobody:x:65534:65534:Kernel
user:x:1000:1000:Example
deploy:x:1001:1001:Deploy
backup:x:1002:1002::/home/backup:/sbin/nologinDomyślnym separatorem jest spacja, więc wiersz połączony tylko dwukropkami ma jedno pole. Tylko wiersze nobody, user i deploy, które mają spację w kolumnie opisu, są przy niej ucinane.
Z -F: dwukropek staje się separatorem, a nazwa konta ($1) i powłoka logowania ($7) rozdzielają się.
awk -F: '{print $1, $7}' users.txtroot /bin/bash
bin /sbin/nologin
daemon /sbin/nologin
nobody /sbin/nologin
user /bin/bash
deploy /bin/bash
backup /sbin/nologinPliki CSV rozdzielone przecinkami dzielisz przez -F,. Po wypisaniu towaru i ilości z zapisów sprzedaży sales.csv nagłówek w pierwszym wierszu (item qty) też dzieli się tak samo i pojawia się razem z nimi.
awk -F, '{print $2, $3}' sales.csvitem qty
keyboard 2
mouse 5
monitor 1
keyboard 1
cable 10
mouse 2
monitor 2-F, nie nadaje się do plików CSV z przecinkami w kolumnach ujętych w cudzysłów. W quoted.csv, z przecinkami w kolumnie towaru, pola dzielą się także przy przecinkach w cudzysłowie, a w drugim polu zostaje tylko początek nazwy towaru.
awk -F, '{print $2}' quoted.csvitem
"cable
keyboardDla takich plików oficjalny podręcznik GNU Awk opisuje zmienną FPAT. Jeśli zamiast separatora zapiszesz kształt jednego pola wyrażeniem regularnym, kolumna w cudzysłowie stanie się jednym polem.
awk -v FPAT='([^,]*)|("[^"]*")' '{print $2}' quoted.csvitem
"cable, usb"
keyboardAby użyć kilku separatorów, zapisz je jako wyrażenie regularne w nawiasach kwadratowych. Gdy separatorami są dwukropek i ukośnik, $NF staje się ostatnią nazwą w ścieżce powłoki logowania (bash, nologin).
awk -F'[:/]' '{print $1, $NF}' users.txtroot bash
bin nologin
daemon nologin
nobody nologin
user bash
deploy bash
backup nologin3️⃣ Wzorce warunkowe polecenia awk
Nawet po wybraniu pól wypisywane są wszystkie wiersze, więc aby zostawić tylko potrzebne, umieść warunek (wzorzec) przed nawiasami klamrowymi. Ciąg znaków między ukośnikami wybiera tylko wiersze, które go zawierają, a po pominięciu akcji wypisywany jest cały wiersz.
awk '/wp-login/' access.log203.0.113.25 - - [29/Oct/2026:00:04:51 +0000] "POST /wp-login.php HTTP/1.1" 404 196 "-" "Mozilla/5.0 (X11; Linux x86_64)"
203.0.113.25 - - [29/Oct/2026:00:04:52 +0000] "POST /wp-login.php HTTP/1.1" 404 196 "-" "Mozilla/5.0 (X11; Linux x86_64)"Wybrane wiersze są takie same jak w poleceniu grep, a awk od razu wybiera z nich pola i je wypisuje. Po przekazaniu potokiem do awk tych samych dwóch wierszy wybranych przez grep zostają tylko IP i kod stanu.
grep wp-login access.log | awk '{print $1, $9}'203.0.113.25 404
203.0.113.25 404Wartości pól porównujesz przez ==, !=, >, >=, <, <=. Wybrano tylko wiersze z kodem stanu ($9) 404 i wypisano IP oraz adres URL żądania.
awk '$9 == 404 {print $1, $7}' access.log203.0.113.25 /wp-login.php
203.0.113.25 /wp-login.php
203.0.113.25 /.envPrzy porównywaniu z ciągiem znaków ujmij wartość w cudzysłów. Wybrano tylko imiona, których drugie pole to pass.
awk '$2 == "pass" {print $1}' scores.txtalice
carol
davePorównanie liczbowe służy do wybierania według zakresu, na przykład numerów kont (UID). Po wybraniu kont z UID od 1000 wzwyż obok zwykłych kont pojawia się też nobody z UID 65534.
awk -F: '$3 >= 1000 {print $1, $3}' users.txtnobody 65534
user 1000
deploy 1001
backup 1002Dwa warunki łączysz przez &&. Po dodaniu warunku poniżej 65534 nobody znika i zostają tylko trzy zwykłe konta oraz ich foldery domowe.
awk -F: '$3 >= 1000 && $3 < 65534 {print $1, $6}' users.txtuser /home/user
deploy /home/deploy
backup /home/backup[~] sprawdza, czy pole pasuje do wyrażenia regularnego, a [!~], czy nie pasuje. Wybór kont, których powłoka logowania ($7) kończy się na bash, oraz kont innych niż nologin daje te same trzy konta.
awk -F: '$7 ~ /bash$/ {print $1}' users.txtroot
user
deployawk -F: '$7 !~ /nologin/ {print $1, $7}' users.txtroot /bin/bash
user /bin/bash
deploy /bin/bashWarunek NR wybiera według numeru wiersza. NR > 1 usuwa wiersz nagłówka CSV i zostają tylko zapisy sprzedaży.
awk -F, 'NR > 1 {print $2, $3}' sales.csvkeyboard 2
mouse 5
monitor 1
keyboard 1
cable 10
mouse 2
monitor 2Wyjście innych poleceń też możesz przekazać potokiem i podzielić tak samo. Po wybraniu rozmiaru pliku ($5) i nazwy ($9) z wyjścia ls -l pierwszy wiersz był pusty, bo pierwszy wiersz ls -l, [total 24], nie ma piątego i dziewiątego pola.
ls -l | awk '{print $5, $9}'
1567 access.log
79 quoted.csv
207 sales.csv
85 scores-copy.txt
85 scores.txt
315 users.txtPo dodaniu NR > 1 pierwszy wiersz znika i zostają tylko rozmiary i nazwy plików.
ls -l | awk 'NR > 1 {print $5, $9}'1567 access.log
79 quoted.csv
207 sales.csv
85 scores-copy.txt
85 scores.txt
315 users.txt4️⃣ awk BEGIN, END, sumy i średnie
Blok BEGIN działa raz przed odczytaniem pierwszego wiersza, a blok END raz po przetworzeniu ostatniego. Sumę dodajesz do zmiennej w każdym wierszu i wypisujesz w END. Poniższy diagram pokazuje kolejność, jaką awk przechodzi dla każdego wiersza przy sumowaniu kwot z zapisów sprzedaży.
$ awk -F, 'NR > 1 {t += $3 * $4} END {printf "total %.2f\n", t}' sales.csvSuma wszystkich wysłanych bajtów ($10) z logu dostępu wyniosła 41298 bajtów. Zmienna użyta po raz pierwszy zaczyna od pustej wartości (jako liczba 0), więc nie trzeba jej ustawiać na 0.
awk '{sum += $10} END {print sum}' access.log41298Jeśli wypiszesz sumę w akcji dla każdego wiersza, a nie w END, każdy wiersz pokaże dotychczasową sumę. Gdy przy każdym dodaniu iloczynu ilości i ceny wypiszesz numer wiersza i t, pojawi się siedem wierszy bez nagłówka, a 886.3 w ostatnim wierszu to suma całkowita.
awk -F, 'NR > 1 {t += $3 * $4; print NR, t}' sales.csv2 90
3 189.5
4 378.5
5 423.5
6 468.5
7 508.3
8 886.3Średnią obliczasz w END, dzieląc sumę przez liczbę wierszy. Suma pięciu wyników wyniosła 343, a średnia 68.6.
awk '{total += $3; n++} END {print total, n, total / n}' scores.txt343 5 68.6BEGIN służy do jednorazowego wypisania wiersza nagłówka nad wynikami. Nad towarami i kwotami (ilość × cena jednostkowa) wiersz item total pojawia się tylko raz.
awk -F, 'BEGIN {print "item", "total"} NR > 1 {print $2, $3 * $4}' sales.csvitem total
keyboard 90
mouse 99.5
monitor 189
keyboard 45
cable 45
mouse 39.8
monitor 378Separator wyjściowy zmieniasz zmienną OFS w BEGIN. Gdy OFS to przecinek, w miejscu przecinków z print pojawia się przecinek zamiast spacji, a lista kont wychodzi w postaci CSV.
awk 'BEGIN {FS = ":"; OFS = ","} {print $1, $3, $7}' users.txtroot,0,/bin/bash
bin,1,/sbin/nologin
daemon,2,/sbin/nologin
nobody,65534,/sbin/nologin
user,1000,/bin/bash
deploy,1001,/bin/bash
backup,1002,/sbin/nologinDo wyrównania kolumn użyj printf. %-10s to ciąg wyrównany do lewej na 10 znakach, %3d to liczba całkowita na 3 znakach, a %8.2f to liczba na 8 znakach z dwoma miejscami po przecinku.
awk -F, 'NR > 1 {printf "%-10s %8.2f\n", $2, $3, $3 * $4}' sales.csvkeyboard 2 90.00
mouse 5 99.50
monitor 1 189.00
keyboard 1 45.00
cable 10 45.00
mouse 2 39.80
monitor 2 378.00W przeciwieństwie do print printf nie przechodzi do nowego wiersza, więc dodaj [\n] na końcu formatu. Bez [\n] nazwy towarów łączą się w jednym wierszu.
awk -F, 'NR > 1 {printf "%s ", $2}' sales.csvkeyboard mouse monitor keyboard cable mouse monitor5️⃣ Analiza logów i opcje polecenia awk
🔲 Analiza logów
Dodawanie do jednej zmiennej daje tylko jedną sumę, więc aby liczyć osobno według IP lub kodu stanu, zbieraj wartości w tablicy. n[$1]++ dodaje 1 do elementu tablicy o kluczu IP, a for (ip in n) w END wyjmuje zebrane elementy po kolei.
awk '{n[$1]++} END {for (ip in n) print n[ip], ip}' access.log | sort -rn4 192.0.2.10
3 203.0.113.25
3 198.51.100.7
2 192.0.2.44Kolejność wyjmowania przez for (ip in n) nie jest określona (według podręcznika GNU Awk), dlatego posortowano według liczby żądań przez sort -rn. Najwięcej miał 192.0.2.10, 4 żądania.
Liczbę według kodu stanu zliczasz tak samo. Były 3 odpowiedzi 404 i 1 odpowiedź 500.
awk '{n[$9]++} END {for (s in n) print s, n[s]}' access.log | sort200 6
301 1
304 1
404 3
500 1Wyjęcie samych kodów stanu i przekazanie ich do sort i uniq -c daje te same liczby.
awk '{print $9}' access.log | sort | uniq -c 6 200
1 301
1 304
3 404
1 500Aby zobaczyć tylko odpowiedzi z błędem, dodaj warunek na kod stanu. Wiersze od 400 wzwyż to trzy żądania szukające wp-login.php i .env oraz jeden błąd 500.
awk '$9 >= 400 {print $9, $1, $7}' access.log404 203.0.113.25 /wp-login.php
404 203.0.113.25 /wp-login.php
404 203.0.113.25 /.env
500 198.51.100.7 /api/search?q=awkSumę wysłanych bajtów na IP też liczysz w tablicy. Najwięcej otrzymał 192.0.2.10, 31340 bajtów.
awk '{b[$1] += $10} END {for (ip in b) print b[ip], ip}' access.log | sort -rn31340 192.0.2.10
5120 192.0.2.44
4250 198.51.100.7
588 203.0.113.25Pole czasu ($4) można porównywać jako ciąg znaków. Wśród wierszy o tej samej dacie i formacie kolejność ciągów odpowiada kolejności czasu, dlatego wybrano tylko cztery żądania od 00:05 do tuż przed 00:10.
awk '$4 >= "[29/Oct/2026:00:05" && $4 < "[29/Oct/2026:00:10" {print $4, $7}' access.log[29/Oct/2026:00:05:30 /contact.html
[29/Oct/2026:00:07:02 /old-page.html
[29/Oct/2026:00:07:15 /.env
[29/Oct/2026:00:09:48 /index.html🔲 Opcje polecenia awk
📌 Główne opcje: wyniki z tego artykułu
| Opcja | Zadanie | Wynik w tym artykule |
|---|---|---|
| -F fs | Ustawia separator pól wejściowych | -F: rozdziela nazwę konta ($1) i powłokę logowania ($7) w users.txt, -F, wypisuje towar i ilość z sales.csv razem z nagłówkiem (item qty) |
| -v var=val | Przypisuje wartość zmiennej przed uruchomieniem programu | Przekazanie zmiennej powłoki min przez -v min=$min wypisuje sześć wierszy z co najmniej 1000 wysłanych bajtów, z -v FPAT=… kolumna w cudzysłowie w quoted.csv staje się jednym polem |
| -i inplace | Zapisuje wyjście do oryginalnego pliku zamiast na ekran | Po gawk -i inplace na ekranie nic się nie pojawia, a kopia scores.txt zmienia się na dwie kolumny, imię i punkty, z -v inplace::suffix=.bak poprzednia treść zostaje w pliku .bak |
| --version | Wypisuje informacje o wersji | Pierwszy wiersz [awk --version] pokazuje wersję GNU Awk 5 |
📎 Pozostałe opcje: według oficjalnego podręcznika
| Opcja | Zadanie |
|---|---|
| -f program-file | Czyta program AWK z pliku zamiast z pierwszego argumentu wiersza poleceń, -f można podać wiele razy |
| -e program-text | Używa tekstu z wiersza poleceń jako źródła programu AWK, można łączyć z funkcjami bibliotek wczytanymi przez -f lub --include |
| -E file | Podobna do -f, ale przetwarzana na końcu i blokuje przypisania zmiennych w wierszu poleceń, dla skryptów #! |
| --traditional | Działa w trybie zgodności, nie rozpoznaje rozszerzeń GNU |
| --posix | Włącza tryb zgodności z dodatkowymi ograniczeniami, na przykład nie rozpoznaje func, synonimu function |
| --lint | Ostrzega przed konstrukcjami wątpliwymi lub trudnymi do przeniesienia do innych implementacji AWK, przy --lint=fatal ostrzeżenia stają się błędami krytycznymi |
| --dump-variables | Zapisuje do pliku posortowaną listę zmiennych globalnych z typami i końcowymi wartościami, bez podania pliku awkvars.out |
🔲 Symulator polecenia awk
W symulatorze poniżej możesz sam wpisywać polecenia awk dla tych samych plików testowych.
🔢 FAQ & polecane treści
Sortowanie
Polecenie sort w Linuksie: sortowanie plików
Polecenie sort sortuje zawartość pliku alfabetycznie lub według wartości liczbowej. Użyj go potem, aby zobaczyć sumy z awk od największej do najmniejszej.
Usuwanie duplikatów
Polecenie uniq w Linuksie
Polecenie uniq łączy kolejne identyczne wiersze w jeden, a z -c liczy, ile razy się powtórzyły.
Podgląd plików
Polecenia head i tail w Linuksie
Polecenia head i tail pokazują tylko kilka pierwszych lub ostatnich wierszy pliku. Często służą do sprawdzania logu dostępu serwera.
ℹ️ Informacja o linkach partnerskich
Treści tej witryny zawierają linki partnerskie. Jeśli odwiedzający kupi produkt lub usługę przez taki link, witryna otrzymuje od sprzedawcy prowizję. Kwota płacona przy tym przez kupującego (podczas promocji spada↓) nie wzrasta. Podane ceny, rabaty i dostępność odnoszą się do chwili pisania i mogą różnić się od stanu faktycznego, dlatego przed zakupem sprawdź je u sprzedawcy. Produkty wybieramy i oceniamy według własnych kryteriów, a prowizja nie wpływa ani na kolejność prezentacji, ani na treść ocen.