Bemästra Linux-kommandoraden och Bash-skriptning · Lektion

awk-funktioner, printf-formatering och rapportgenerering

Definiera egna funktioner och använd printf för att skapa välformaterade tabellrapporter från rådataströmmar.

Lektion 4 av 413 steg

awk-funktioner, printf-formatering och rapportgenerering är en gratis lektion i Bemästra Linux-kommandoraden och Bash-skriptning på CoddyKit. Detta är lektion 4 av 4. Ni kan läsa hela lektionen gratis nedan och sedan öva praktiskt i webbläsaren med en inbyggd kodredigerare och en AI-handledare som är tillgänglig dygnet runt. Den ingår i lärvägen för Bemästra Linux-kommandoraden och Bash-skriptning, och Era framsteg synkroniseras mellan webben och CoddyKit-appen. Kursen i Bemästra Linux-kommandoraden och Bash-skriptning innehåller totalt 4 lektioner.

Varför funktioner och printf är viktiga i awk

När dina awk-program växer från enradsuttryck till pipeliner med flera steg blir två funktioner nödvändiga: användardefinierade funktioner och printf-formatering.

Funktioner låter dig kapsla in återanvändbar logik — en procentberäknare, en funktion som tar bort överflödiga blanksteg eller en enhetsomvandlare — så att du skriver den en gång och kan anropa den var som helst i samma program. Med printf kan du styra exakt hur utdata ser ut: kolumnbredder, decimaler, utfyllnad och justering.

Tillsammans omvandlar de råa loggrader till välformaterade, lättlästa rapporter som ingenjörer och chefer kan agera utifrån.

  • Funktioner minskar duplicering och gör program möjliga att testa isolerat.
  • printf justerar data i kolumner med fast bredd, så att rapporter förblir lättlästa även när indatans längder varierar.
  • Båda funktionerna fungerar i POSIX awk, gawk och mawk — inga tillägg behövs.

Definiera en användarfunktion i awk

En användarfunktion deklareras med nyckelordet function, före eller efter valfria mönster-åtgärdsregler. Syntaxen är:

function name(param1, param2,    local1, local2) {
    # body
    return value
}

Ett viktigt awk-idiom: lokala variabler är helt enkelt extra parametrar med extra blanksteg framför sig. Det finns inget nyckelord local — konventionen med extra blanksteg visar att parametrarna är lokala och inte argument som anroparen skickar in.

  • Alla variabler som inte deklareras som parametrar är globala som standard.
  • Funktioner kan anropa sig själva rekursivt.
  • return är valfritt; utan det returnerar funktionen en tom sträng.
#!/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)
}
'

Funktioner med stränglogik

Funktioner är särskilt användbara för återkommande strängoperationer. Tänk på att ta bort inledande och avslutande blanksteg — något som rå loggdata ofta behöver innan jämförelser eller rapportering.

Exemplet nedan definierar trim(s) med hjälp av gsub och använder den för varje post, så att huvudregeln förblir ren och lättläst.

  • gsub(regex, replacement, target) ändrar target på plats och returnerar antalet ersättningar.
  • Om du placerar reguljära uttryckets logik i en funktion kan regelblocket fokusera på affärslogiken.
  • Du kan enhetstesta genom att skicka egenskapade strängar direkt i ett BEGIN-block.
#!/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
}
'

Introduktion till printf i awk

printf i awk följer samma konventioner som C och Bashs inbyggda printf. Den viktigaste skillnaden mot print är att printf aldrig lägger till en radbrytning automatiskt — du måste själv lägga till \n.

Vanliga formatangivelser:

  • %s — sträng
  • %d — heltal
  • %f — flyttal
  • %e — vetenskaplig notation
  • %g — den kortare av %f/%e

Bredd och precision styrs genom att placera siffror mellan % och formatangivelsen: %-20s vänsterjusterar i ett fält med 20 tecken; %8.2f ger ett flyttal i ett 8 tecken brett fält med 2 decimaler.

#!/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
}
'

Skapa en rapporthuvudrad med printf

En välformaterad rapport behöver en rubrikrad och en avskiljarrad. Blocket BEGIN är den naturliga platsen att skriva ut dessa på — det körs en gång innan någon indata bearbetas.

Tricket är att matcha bredderna i rubriken exakt med bredderna som används i dataraderna. Genom att definiera breddkonstanter i BEGIN (eller i en funktion) hålls allt synkroniserat när du senare justerar kolumnbredderna.

  • Använd printf med en avskiljarsträng av bindestreck för att rita skiljelinjen.
  • Avsluta alltid rubrikrader med \n.
  • Kombinera en BEGIN-rubrik, rader per post och en END-sidfot för en komplett rapportstruktur.
#!/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
}
'

Användarfunktioner för formateringslogik

När du bygger rapporter med flera kolumner behöver du ofta samma formateringsanrop på flera ställen — till exempel för att visa en procentstapel eller omvandla byte till en lättläst enhet. Genom att placera detta i en funktion undviker du duplicering och gör formatet enkelt att ändra globalt.

Funktionen nedan omvandlar byte till KB, MB eller GB och returnerar en formaterad sträng. Den anropande regeln skickar helt enkelt in $NF (det sista fältet) och skriver ut det som returneras.

#!/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) }
'

Samla data i matriser innan utskrift

Verkliga rapporter behöver ofta totalsummor, medelvärden eller rangordningar – värden som inte kan beräknas förrän alla indata har lästs in. Mönstret är:

  • Samla data i associativa matriser under fasen där varje post behandlas.
  • Beräkna härledda värden (medelvärden, procenttal) i END.
  • Skriv endast ut formaterade resultat från END, så att kolumnbredderna kan anpassas efter hela datamängden.

Detta mönster med fördröjd utskrift är ett av de mest kraftfulla idiomen när man genererar rapporter i awk. Exemplet nedan räknar antalet förfrågningar och antalet svarbyte per HTTP-statuskod från en åtkomstlogg.

#!/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]
}
'

Rekursiva funktioner i awk

awk stöder rekursion. Ett klassiskt användningsområde är att beräkna fakulteter eller generera upprepade strängar (till exempel en rad med bindestreck som är lika bred som terminalen). Rekursion är mindre vanligt i awk än i programspråk för allmänna ändamål, men fungerar korrekt och finns i alla större implementationer.

Exemplet nedan definierar en rekursiv funktion repeat(s, n) och använder den för att rita avgränsningslinjer som automatiskt matchar bredden på den bredaste dataraden – inga hårdkodade bindestreck behövs.

  • Undvik djup rekursion för stora indata – awk har ingen optimering av svansanrop.
  • För enkel upprepning är sprintf("%*s", n, "") tillsammans med gsub(/ /, "-") snabbare, men mindre pedagogiskt.
#!/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/null

printf till en fil eller pipeline i awk

aw:s printf (och print) kan omdirigera utdata till filer eller skicka den till skalkommandon via en pipe – direkt inifrån awk-programmet, utan att hela programmet behöver köras i ett subshell.

  • printf "..." > "file.txt" – skriver till en fil (tömmer filen en gång och lägger sedan till data under samma körning).
  • printf "..." >> "file.txt" – lägger till data i en fil.
  • printf "..." | "sort -rn" – skickar data till ett skalkommando via en pipe; awk håller pipen öppen mellan posterna och stänger den när programmet avslutas.

Ett vanligt mönster är att dela upp en enda genomgång av en logg i flera utdatafiler, en per status eller värd, så att man slipper läsa igenom en stor fil flera gånger.

#!/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"

Generera en CSV-rapport med awk

Alla rapporter är inte avsedda för människor. Ibland måste utdata vara maskinläsbar CSV som kan matas in i ett kalkylark eller en efterföljande pipeline. aw:s printf hanterar detta smidigt: ange OFS till ett kommatecken eller styr varje avgränsare uttryckligen.

Två viktiga regler för robust CSV från awk:

  • Omslut fält som kan innehålla kommatecken eller radbrytningar med dubbla citattecken.
  • Escape:a alla bokstavliga dubbla citattecken i ett fält genom att dubblera dem: He said ""hello"".

Funktionen csv_field(s) nedan kapslar in denna citeringslogik, så att den kan användas konsekvent för varje strängfält.

#!/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
}
'

Från början till slut: en komplett åtkomstloggsrapport

Det här slutliga exemplet knyter ihop allt: användardefinierade funktioner, formatering med printf, insamling i matriser samt en strukturerad sidhuvud- och sidfotsdel. Indata är en förenklad åtkomstlogg från webben; utdata är en lättläst sammanfattningstabell med totalsummor per metod och en rad med totalsumman.

Viktiga tekniker:

  • count[method]++ och bytes[method] += size samlar statistik per metod i en enda genomgång.
  • human_bytes() (från en tidigare scen) omvandlar byte-totaler.
  • printf med matchande bredder i sidhuvud, data och sidfot ser till att tabellen förblir justerad oavsett indatans storlek.
  • Blocket END itererar över matrisen med for (k in arr) och skriver ut sorterade resultat via en pipe till sort.
#!/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)
}
'

Kunskapskontroll: lokala variabler i awk-funktioner

Vilket av följande deklarerar korrekt result som en lokal variabel i en awk-funktion enligt POSIX-konventionen?

Lektionssammanfattning: awk-funktioner, printf och rapportgenerering

I den här lektionen har Ni lärt Er att skriva awk-program med produktionskvalitet som genererar formaterade rapporter från rådataströmmar. Detta bör Ni ta med Er:

  • Användardefinierade funktioner deklareras med function name(params, locals). Extra blanksteg före parametrarna för lokala variabler är POSIX-konventionen – det finns inget nyckelord local.
  • printf ger exakt kontroll över utdata: bredd (%10s), justering (%-10s) och precision (%8.2f). Kom ihåg att lägga till \n uttryckligen.
  • Använd BEGIN för att skriva ut sidhuvuden och avgränsare, regler per post för att samla data och END för att beräkna totalsummor och skriva ut den färdiga rapporten.
  • Omdirigera utdata från printf till filer (> file) eller pipelines (| "sort") inifrån awk för att dela upp rapporter eller efterbehandla utdata.
  • Omslut strängfält med en hjälpfunktion csv_field() när Ni skriver CSV, så att inbäddade kommatecken och citattecken hanteras säkert.
  • Funktioner som omvandlar enheter (human_bytes), upprepar tecken (repeat) eller rensar strängar (trim, csv_field) är värda att spara i ett personligt awk-bibliotek – de kan enkelt kombineras mellan projekt.
Gratis att börja

Lär dig Bash med en AI-lärare – gratis

Skriv och kör riktig kod i webbläsaren, få omedelbar hjälp av en AI-lärare dygnet runt och fortsätt där du slutade – på webben eller i appen.

Kurser
22
Lektioner
88

Vanliga frågor

Är lektionen ”awk-funktioner, printf-formatering och rapportgenerering” gratis?

Ja – hela texten till ”awk-funktioner, printf-formatering och rapportgenerering” kan läsas gratis här på webben. Om Ni vill öva interaktivt med en inbyggd kodredigerare och en AI-handledare som är tillgänglig dygnet runt och låsa upp resten av kursen i Bemästra Linux-kommandoraden och Bash-skriptning, kan Ni uppgradera till CoddyKit PRO. Kursen i Bemästra Linux-kommandoraden och Bash-skriptning innehåller totalt 4 lektioner.

Vad lär jag mig i ”awk-funktioner, printf-formatering och rapportgenerering”?

Definiera egna funktioner och använd printf för att skapa välformaterade tabellrapporter från rådataströmmar. Ni övar på Bemästra Linux-kommandoraden och Bash-skriptning med praktisk kod som körs direkt i webbläsaren, medan en AI-handledare som är tillgänglig dygnet runt svarar på Era frågor under lektionen.

Behöver jag någon erfarenhet för att börja lära mig Bemästra Linux-kommandoraden och Bash-skriptning?

Du behöver inga förkunskaper. Utbildningen i Bemästra Linux-kommandoraden och Bash-skriptning på CoddyKit är upplagd för allt från nybörjare till avancerade elever, så att du kan börja här eller från början och gå fram i din egen takt. Detta är lektion 4 av 4.

Hur lång tid tar lektionen ”awk-funktioner, printf-formatering och rapportgenerering”?

De flesta CoddyKit-lektioner tar cirka 5–10 minuter. Varje lektion är kort och interaktiv, så att du gör stadiga framsteg och kan fortsätta precis där du slutade – på webben eller i appen.

Kan jag skriva och köra kod i den här Bemästra Linux-kommandoraden och Bash-skriptning-lektionen?

Ja. Varje Bemästra Linux-kommandoraden och Bash-skriptning-lektion innehåller en inbyggd kodredigerare, så att du kan skriva och köra riktig kod direkt i webbläsaren och få omedelbar AI-feedback – utan lokal installation.

Alla lektioner i den här kursen

  1. Poster, fält och anpassade avgränsare i awk
  2. Mönster, intervall och BEGIN-/END-block
  3. Aggregering med awk-arrayer och gruppering
  4. awk-funktioner, printf-formatering och rapportgenerering
← Tillbaka till Bemästra Linux-kommandoraden och Bash-skriptning