Funkcje awk, formatowanie printf i generowanie raportów
Definiuj własne funkcje i używaj printf do tworzenia dopracowanych raportów tabelarycznych z surowych strumieni danych.
Funkcje awk, formatowanie printf i generowanie raportów to bezpłatna lekcja DevOps Bootcamp na CoddyKit. To lekcja 4 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 DevOps Bootcamp, a Twój postęp synchronizuje się między webem a aplikacją CoddyKit. Kurs DevOps Bootcamp zawiera 4 lekcji w sumie.
Dlaczego funkcje i printf mają znaczenie w awk
Gdy programy awk rozrastają się od jednowierszowych poleceń do wieloetapowych potoków, niezbędne stają się dwie funkcje: funkcje definiowane przez użytkownika i formatowanie za pomocą printf.
Funkcje pozwalają zamknąć wielokrotnie używaną logikę — kalkulator procentów, funkcję usuwającą białe znaki czy konwerter jednostek — aby napisać ją raz i wywoływać w dowolnym miejscu tego samego programu. printf pozwala dokładnie kontrolować wygląd danych wyjściowych: szerokość kolumn, liczbę miejsc dziesiętnych, dopełnienie i wyrównanie.
Razem przekształcają surowe wiersze dzienników w dopracowane, czytelne raporty, na podstawie których inżynierowie i menedżerowie mogą podejmować działania.
- Funkcje ograniczają powielanie kodu i ułatwiają niezależne testowanie programów.
printfwyrównuje dane w kolumnach o stałej szerokości, dzięki czemu raporty pozostają czytelne nawet przy różnej długości danych wejściowych.- Obie funkcje działają w POSIX awk, gawk i mawk — nie wymagają rozszerzeń.
Definiowanie funkcji użytkownika w awk
Funkcję użytkownika deklaruje się za pomocą słowa kluczowego function, przed regułami wzorzec-działanie lub po nich. Składnia wygląda następująco:
function name(param1, param2, local1, local2) {
# body
return value
}Ważny idiom awk: zmienne lokalne są po prostu dodatkowymi parametrami oddzielonymi od pozostałych większą ilością białych znaków. Nie ma słowa kluczowego local — konwencja dodatkowych spacji sygnalizuje, że te parametry są lokalne, a nie przekazywane przez wywołującego jako argumenty.
- Wszystkie zmienne, które nie zostały zadeklarowane jako parametry, są domyślnie globalne.
- Funkcje mogą wywoływać same siebie rekurencyjnie.
returnjest opcjonalne; bez niego funkcja zwraca pusty ciąg znaków.
#!/usr/bin/env bash
# Demonstrate a simple awk user function
echo '10 3
7 0
100 4' | awk '
function divide(a, b, result) {
if (b == 0) return "ERR"
result = a / b
return result
}
{
print $1 "/" $2 " = " divide($1, $2)
}
'Funkcje z logiką operującą na ciągach znaków
Funkcje są szczególnie przydatne przy wielokrotnie wykonywanych operacjach na ciągach znaków. Rozważmy usuwanie początkowych i końcowych białych znaków — surowe dane z dzienników często wymagają tego przed porównaniem lub wygenerowaniem raportu.
Poniższy przykład definiuje funkcję trim(s) za pomocą gsub i używa jej dla każdego rekordu, dzięki czemu główna reguła pozostaje przejrzysta i czytelna.
gsub(regex, replacement, target)modyfikuje target bezpośrednio i zwraca liczbę podstawień.- Umieszczenie logiki wyrażenia regularnego w funkcji pozwala zachować koncentrację bloku reguły na logice biznesowej.
- Testy jednostkowe można przeprowadzać, przekazując ręcznie przygotowane ciągi bezpośrednio w bloku
BEGIN.
#!/usr/bin/env bash
# trim() strips leading/trailing spaces; used to normalise CSV-like data
printf ' alice , 90 \n bob , 85 \n carol , 92 \n' | awk -F',' '
function trim(s) {
gsub(/^[ \t]+|[ \t]+$/, "", s)
return s
}
{
name = trim($1)
score = trim($2)
print name, score
}
'Wprowadzenie do printf w awk
printf w awk stosuje te same konwencje co C i wbudowane printf powłoki Bash. Najważniejsza różnica w porównaniu z print polega na tym, że printf nigdy nie dodaje automatycznie znaku nowego wiersza — trzeba samodzielnie dodać \n.
Typowe specyfikatory formatu:
%s— ciąg znaków%d— liczba całkowita%f— liczba zmiennoprzecinkowa%e— notacja naukowa%g— krótszy format spośród%f/%e
Szerokość i precyzję określa się, umieszczając liczby między % a specyfikatorem: %-20s wyrównuje do lewej w polu o szerokości 20 znaków; %8.2f daje liczbę zmiennoprzecinkową o szerokości 8 znaków i 2 miejscach dziesiętnych.
#!/usr/bin/env bash
# printf format specifiers in awk
echo '' | awk '
BEGIN {
printf "%s\n", "plain string"
printf "%10s\n", "right-pad"
printf "%-10s|\n", "left-pad"
printf "%8.2f\n", 3.14159
printf "%08d\n", 42
printf "%e\n", 123456.789
}
'Budowanie nagłówka raportu za pomocą printf
Dopracowany raport potrzebuje wiersza nagłówka i linii oddzielającej. Blok BEGIN jest naturalnym miejscem do ich wypisania — jest wykonywany raz, przed przetworzeniem jakichkolwiek danych wejściowych.
Sztuczka polega na dokładnym dopasowaniu szerokości w nagłówku do szerokości używanych w wierszach danych. Zdefiniowanie stałych szerokości w BEGIN (lub w funkcji) pozwala zachować spójność po późniejszej zmianie szerokości kolumn.
- Użyj
printfz separatorem złożonym z myślników, aby narysować linię rozdzielającą. - Zawsze kończ wiersze nagłówka za pomocą
\n. - Połącz nagłówek w
BEGIN, wiersze dla poszczególnych rekordów i stopkę wEND, aby utworzyć kompletną strukturę raportu.
#!/usr/bin/env bash
# Print a report header in BEGIN, data rows per-record, total in END
printf 'alice 9200 12\nbob 8750 10\ncarol 10400 14\n' | awk '
BEGIN {
printf "%-10s %10s %6s %12s\n", "Name", "Salary", "Months", "Annual"
printf "%s\n", "----------------------------------------------"
total = 0
}
{
annual = $2 * $3
total += annual
printf "%-10s %10d %6d %12d\n", $1, $2, $3, annual
}
END {
printf "%s\n", "----------------------------------------------"
printf "%-10s %10s %6s %12d\n", "TOTAL", "", "", total
}
'Funkcje użytkownika do obsługi formatowania
Podczas tworzenia raportów wielokolumnowych często trzeba wywoływać to samo formatowanie w kilku miejscach — na przykład wyświetlać pasek procentowy lub konwertować bajty na jednostkę czytelną dla człowieka. Umieszczenie tej logiki w funkcji eliminuje powielanie kodu i ułatwia globalną zmianę formatu.
Poniższa funkcja konwertuje bajty na KB, MB lub GB i zwraca sformatowany ciąg znaków. Reguła wywołująca przekazuje po prostu $NF (ostatnie pole) i wypisuje otrzymany wynik.
#!/usr/bin/env bash
# human_bytes() converts raw byte counts to KB/MB/GB strings
printf 'var.log 1024\naccess.log 2097152\ncore.dump 1073741824\ntiny.txt 512\n' | awk '
function human_bytes(n, s) {
if (n >= 1073741824) { s = sprintf("%.1f GB", n/1073741824) }
else if (n >= 1048576) { s = sprintf("%.1f MB", n/1048576) }
else if (n >= 1024) { s = sprintf("%.1f KB", n/1024) }
else { s = sprintf("%d B", n) }
return s
}
BEGIN { printf "%-20s %10s\n", "File", "Size"; print "------------------------------" }
{ printf "%-20s %10s\n", $1, human_bytes($2) }
'Gromadzenie danych w tablicach przed wyświetleniem
Rzeczywiste raporty często wymagają obliczenia sum, średnich lub rankingów — wartości, których nie można obliczyć, dopóki nie zostaną odczytane wszystkie dane wejściowe. Schemat postępowania jest następujący:
- Podczas przetwarzania poszczególnych rekordów należy gromadzić dane w tablicach asocjacyjnych.
- Wartości pochodne (średnie, wartości procentowe) należy obliczać w
END. - Sformatowane dane wyjściowe należy generować wyłącznie w
END, aby szerokości mogły zostać dopasowane do całego zbioru danych.
Schemat odroczonego wyjścia jest jednym z najbardziej użytecznych idiomów podczas generowania raportów w awk. Poniższy przykład zlicza liczbę żądań i bajty odpowiedzi dla poszczególnych kodów statusu HTTP w dzienniku dostępu.
#!/usr/bin/env bash
# Summarise an nginx-style access log by HTTP status code
printf '127.0.0.1 200 1234\n127.0.0.1 404 512\n10.0.0.2 200 8900\n10.0.0.3 500 256\n10.0.0.4 200 4096\n10.0.0.5 404 300\n' | awk '
{
status = $2
bytes = $3
count[status]++
total_bytes[status] += bytes
}
END {
printf "%-8s %8s %14s\n", "Status", "Requests", "Total Bytes"
print "-----------------------------------"
for (s in count)
printf "%-8s %8d %14d\n", s, count[s], total_bytes[s]
}
'Funkcje rekurencyjne w awk
awk obsługuje rekurencję. Klasycznym zastosowaniem jest obliczanie silni lub generowanie powtarzalnych ciągów znaków (na przykład linii myślników o długości dopasowanej do szerokości terminala). Rekurencja jest w awk stosowana rzadziej niż w językach ogólnego przeznaczenia, ale działa poprawnie i jest dostępna we wszystkich głównych implementacjach.
Poniższy przykład definiuje rekurencyjną funkcję repeat(s, n) i używa jej do rysowania linii oddzielających, które automatycznie dopasowują się do szerokości najdłuższego wiersza danych — nie trzeba określać liczby myślników na stałe.
- Należy unikać głębokiej rekurencji dla dużych danych wejściowych — awk nie wykonuje optymalizacji wywołań ogonowych.
- W przypadku prostego powtarzania szybsze, choć mniej obrazowe, jest użycie
sprintf("%*s", n, "")wraz zgsub(/ /, "-").
#!/usr/bin/env bash
# Recursive repeat() to build separator lines dynamically
awk '
function repeat(s, n, acc) {
if (n <= 0) return ""
acc = s repeat(s, n-1)
return acc
}
BEGIN {
header = sprintf("%-15s %10s %10s", "Metric", "Current", "Target")
sep = repeat("-", length(header))
print sep
print header
print sep
printf "%-15s %10.1f %10.1f\n", "CPU %", 72.4, 60.0
printf "%-15s %10.1f %10.1f\n", "Mem GB", 14.2, 16.0
printf "%-15s %10d %10d\n", "Open FDs", 1024, 800
print sep
}
' /dev/nullprintf do pliku lub potoku wewnątrz awk
Polecenia printf (oraz print) w awk mogą przekierowywać dane wyjściowe do plików lub przesyłać je potokiem do poleceń powłoki — bezpośrednio z programu awk, bez umieszczania całego programu w podpowłoce.
printf "..." > "file.txt"— zapisuje dane do pliku (obcina plik raz, a następnie dopisuje dane w ramach tego samego uruchomienia).printf "..." >> "file.txt"— dopisuje dane do pliku.printf "..." | "sort -rn"— przesyła dane potokiem do polecenia powłoki; awk utrzymuje potok otwarty między rekordami i zamyka go po zakończeniu programu.
Typowym schematem jest rozdzielenie jednego przebiegu przez dziennik na wiele plików wyjściowych — osobnych dla poszczególnych statusów lub hostów — co pozwala uniknąć wielokrotnego skanowania dużego pliku.
#!/usr/bin/env bash
# Route records into per-status files using print redirection
tmpdir=$(mktemp -d)
trap 'rm -rf "$tmpdir"' EXIT
printf '200 /index.html\n404 /missing\n200 /api/data\n500 /crash\n404 /lost\n' | awk -v out="$tmpdir" '
{
file = out "/" $1 ".log"
printf "%-6s %s\n", $1, $2 > file
}
END {
close(file) # flush all open handles
}
'
echo "=== 200 ==="
cat "$tmpdir/200.log"
echo "=== 404 ==="
cat "$tmpdir/404.log"Generowanie raportu CSV za pomocą awk
Nie wszystkie raporty są przeznaczone dla ludzi. Czasami dane wyjściowe muszą mieć postać czytelnego maszynowo pliku CSV, który zasila arkusz kalkulacyjny lub kolejny etap potoku. Polecenie printf w awk dobrze sobie z tym radzi: można ustawić OFS na przecinek albo jawnie kontrolować każdy separator.
Dwie ważne zasady tworzenia poprawnego pliku CSV w awk:
- Pola, które mogą zawierać przecinki lub znaki nowego wiersza, należy ująć w podwójne cudzysłowy.
- Każdy dosłowny podwójny cudzysłów wewnątrz pola należy poprzedzić drugim podwójnym cudzysłowem:
He said ""hello"".
Funkcja csv_field(s) zawiera tę logikę cudzysłowów, dzięki czemu można stosować ją spójnie do każdego pola tekstowego.
#!/usr/bin/env bash
# Emit a valid CSV report from space-separated input
printf 'alice engineer 95000\nbob "sales,mgr" 82000\ncarol developer 110000\n' | awk '
function csv_field(s, safe) {
safe = s
gsub(/"/, "\"\"" , safe) # double any embedded quotes
return "\"" safe "\"" # wrap in quotes
}
BEGIN { print "Name,Role,Salary" }
{
printf "%s,%s,%d\n", csv_field($1), csv_field($2), $3
}
'Od początku do końca: kompletny raport z dziennika dostępu
Ten końcowy przykład łączy wszystkie elementy: funkcje użytkownika, formatowanie za pomocą printf, gromadzenie danych w tablicach oraz uporządkowany nagłówek i stopkę. Dane wejściowe to uproszczony dziennik dostępu do witryny, a dane wyjściowe to czytelna dla człowieka tabela podsumowania z sumami dla poszczególnych metod i wierszem z sumą całkowitą.
Wykorzystane najważniejsze techniki:
count[method]++orazbytes[method] += sizegromadzą statystyki dla poszczególnych metod w jednym przebiegu.human_bytes()(z wcześniejszej sceny) konwertuje sumy bajtów.printfz jednakowymi szerokościami w nagłówku, danych i stopce zapewnia wyrównanie tabeli niezależnie od rozmiaru danych wejściowych.- Blok
ENDiteruje po tablicy za pomocąfor (k in arr)i generuje posortowane dane wyjściowe, przesyłając je potokiem dosort.
#!/usr/bin/env bash
# Full access log report: method breakdown with human-readable bytes
printf 'GET /index 200 4096\nPOST /api 201 512\nGET /img 200 102400\nDELETE /item 204 0\nPOST /login 401 256\nGET /style 200 8192\n' | awk '
function human_bytes(n, s) {
if (n >= 1048576) s = sprintf("%.1f MB", n/1048576)
else if (n >= 1024) s = sprintf("%.1f KB", n/1024)
else s = sprintf("%d B", n)
return s
}
{
method = $1
size = $4
count[method]++
bytes[method] += size
grand_count++
grand_bytes += size
}
END {
fmt = "%-10s %8s %15s\n"
sep = "----------------------------------"
printf fmt, "Method", "Requests", "Bytes"
print sep
for (m in count)
printf "%-10s %8d %15s\n", m, count[m], human_bytes(bytes[m])
print sep
printf "%-10s %8d %15s\n", "TOTAL", grand_count, human_bytes(grand_bytes)
}
'Sprawdzenie wiedzy: zmienne lokalne w funkcjach awk
Która z poniższych metod poprawnie deklaruje result jako zmienną lokalną wewnątrz funkcji awk, zgodnie z konwencją POSIX?
Podsumowanie lekcji: funkcje awk, printf i generowanie raportów
W tej lekcji nauczyli się Państwo pisać profesjonalne programy awk, które generują sformatowane raporty z nieprzetworzonych strumieni danych. Oto najważniejsze informacje:
- Funkcje użytkownika deklaruje się za pomocą
function name(params, locals). Dodatkowe odstępy przed parametrami zmiennych lokalnych są konwencją POSIX — nie istnieje słowo kluczowelocal. - printf zapewnia precyzyjną kontrolę nad danymi wyjściowymi: szerokością (
%10s), wyrównaniem (%-10s) i precyzją (%8.2f). Należy pamiętać o jawnym dodaniu\n. - Należy używać BEGIN do wyświetlania nagłówków i separatorów, reguł dla poszczególnych rekordów do gromadzenia danych oraz END do obliczania sum i wyświetlania gotowego raportu.
- Dane wyjściowe
printfmożna przekierowywać z poziomu awk do plików (> file) lub potoków (| "sort"), aby dzielić raporty lub przetwarzać dane wyjściowe. - Pola tekstowe należy opakowywać pomocniczą funkcją csv_field() podczas generowania plików CSV, aby bezpiecznie obsługiwać osadzone przecinki i cudzysłowy.
- Warto przechowywać w osobistej bibliotece awk funkcje konwertujące jednostki (
human_bytes), powtarzające znaki (repeat) lub oczyszczające ciągi znaków (trim,csv_field) — można je łatwo łączyć w różnych projektach.
Często zadawane pytania
Czy lekcja „Funkcje awk, formatowanie printf i generowanie raportów” jest bezpłatna?
Tak — pełny tekst „Funkcje awk, formatowanie printf i generowanie raportów” 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 DevOps Bootcamp, przejdź na CoddyKit PRO. Kurs DevOps Bootcamp zawiera 4 lekcji w sumie.
Co nauczysz się w „Funkcje awk, formatowanie printf i generowanie raportów”?
Definiuj własne funkcje i używaj printf do tworzenia dopracowanych raportów tabelarycznych z surowych strumieni danych. Ćwiczysz DevOps Bootcamp 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ąć DevOps Bootcamp?
Nie wymagamy żadnego doświadczenia. DevOps Bootcamp 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 4 z 4.
Ile czasu zajmuje lekcja „Funkcje awk, formatowanie printf i generowanie raportów”?
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 DevOps Bootcamp?
Tak. Każda lekcja DevOps Bootcamp 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
- Rekordy, pola i niestandardowe separatory w awk
- Wzorce, zakresy oraz bloki BEGIN/END
- Agregowanie za pomocą tablic i grupowania w awk
- Funkcje awk, formatowanie printf i generowanie raportów