0Pricing
Linux Command Line & Bash Scripting Mastery · 강의

awk 함수, printf 서식 지정 및 보고서 생성

사용자 함수를 정의하고 printf를 사용하여 원시 데이터 스트림에서 보기 좋은 표 형식의 보고서를 출력합니다.

awk 함수, printf 서식 지정 및 보고서 생성은(는) CoddyKit의 무료 Linux Command Line & Bash Scripting Mastery 강의입니다. 이것은 4개 중 4번째 강의입니다. 아래에서 전체 강의를 무료로 읽을 수 있으며, 내장 코드 에디터와 24/7 AI 튜터와 함께 브라우저에서 직접 실습할 수 있습니다. 이 강의는 Linux Command Line & Bash Scripting Mastery 학습 경로의 일부이며, 진행 상황이 웹과 CoddyKit 앱에 동기화됩니다. Linux Command Line & Bash Scripting Mastery 강의에는 총 4개의 강의가 포함되어 있습니다.

awk에서 함수와 printf가 중요한 이유

awk 프로그램이 한 줄짜리 명령에서 여러 단계의 파이프라인으로 커지면 두 가지 기능이 필수적이 됩니다. 바로 사용자 정의 함수와 printf 형식 지정입니다.

함수를 사용하면 재사용 가능한 로직(백분율 계산기, 문자열 정리 함수, 단위 변환기 등)을 캡슐화할 수 있으므로 한 번 작성한 뒤 같은 프로그램 어디에서나 호출할 수 있습니다. printf를 사용하면 출력의 모양을 정확하게 제어할 수 있습니다. 열 너비, 소수 자릿수, 여백, 정렬 등을 지정할 수 있습니다.

이 두 기능을 함께 사용하면 원시 로그 줄을 엔지니어와 관리자가 활용할 수 있는 깔끔하고 읽기 쉬운 보고서로 바꿀 수 있습니다.

  • 함수는 중복을 줄이고 프로그램을 독립적으로 테스트할 수 있게 합니다.
  • printf는 데이터를 고정 너비 열에 맞춰 정렬하므로 입력 길이가 달라도 보고서를 읽기 쉽게 유지합니다.
  • 두 기능 모두 POSIX awk, gawk, mawk에서 확장 기능 없이 사용할 수 있습니다.

awk에서 사용자 함수 정의하기

사용자 함수는 function 키워드로 선언하며, 패턴-동작 규칙의 앞이나 뒤에 둘 수 있습니다. 문법은 다음과 같습니다.

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

awk의 중요한 관용적 사용법은 다음과 같습니다. 지역 변수는 매개변수 뒤에 공백을 더 넣은 추가 매개변수로 표현합니다. local 키워드는 없습니다. 공백을 추가하는 관례로 해당 매개변수가 호출자가 전달하는 인수가 아니라 지역 변수임을 나타냅니다.

  • 매개변수로 선언하지 않은 모든 변수는 기본적으로 전역입니다.
  • 함수는 자기 자신을 재귀적으로 호출할 수 있습니다.
  • return은 선택 사항입니다. 사용하지 않으면 함수는 빈 문자열을 반환합니다.
#!/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)
}
'

문자열 로직을 사용하는 함수

함수는 반복적인 문자열 작업에 특히 유용합니다. 원시 로그 데이터에서 비교나 보고 전에 자주 수행해야 하는 작업인 앞뒤 공백 제거를 예로 들어 보겠습니다.

아래 예에서는 gsub를 사용하여 trim(s)을 정의하고 모든 레코드에서 호출하므로, 주요 규칙을 깔끔하고 읽기 쉽게 유지할 수 있습니다.

  • gsub(regex, replacement, target)은 target을 직접 수정하고 치환 횟수를 반환합니다.
  • 정규 표현식 로직을 함수 안에 넣으면 규칙 블록이 업무 로직에 집중할 수 있습니다.
  • 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
}
'

awk의 printf 소개

awk의 printf는 C 및 Bash에 내장된 printf와 동일한 규칙을 따릅니다. print와 다른 핵심적인 점은 printf가 자동으로 줄 바꿈을 추가하지 않는다는 것입니다. 직접 \n을 추가해야 합니다.

일반적인 형식 지정자는 다음과 같습니다.

  • %s — 문자열
  • %d — 정수
  • %f — 부동 소수점 수
  • %e — 과학적 표기법
  • %g — %f와 %e 중 더 짧은 형식

너비와 정밀도는 %와 지정자 사이에 숫자를 넣어 제어합니다. %-20s는 20자 필드에서 왼쪽 정렬하고, %8.2f는 너비 8에 소수점 이하 2자리인 부동 소수점 형식을 지정합니다.

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

printf로 보고서 헤더 만들기

완성도 높은 보고서에는 헤더 행과 구분선이 필요합니다. BEGIN 블록은 입력을 처리하기 전에 한 번 실행되므로 이를 출력하기에 적합한 위치입니다.

핵심은 헤더의 너비를 데이터 행에 사용되는 너비와 정확히 일치시키는 것입니다. BEGIN 블록이나 함수에서 너비 상수를 정의하면 나중에 열 너비를 조정할 때도 모든 부분을 일관되게 유지할 수 있습니다.

  • 대시로 구성된 구분 문자열과 함께 printf를 사용하여 구분선을 그립니다.
  • 헤더 줄은 항상 \n으로 끝냅니다.
  • BEGIN 헤더, 레코드별 행, END 바닥글을 결합하여 완전한 보고서 구조를 만듭니다.
#!/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
}
'

형식 지정 로직을 위한 사용자 함수

여러 열로 구성된 보고서를 만들다 보면 같은 형식 지정 호출을 여러 곳에서 사용해야 하는 경우가 많습니다. 예를 들어 백분율 막대를 표시하거나 바이트를 사람이 읽기 쉬운 단위로 변환하는 경우입니다. 이를 함수 안에 넣으면 중복을 피하고 형식을 전역적으로 쉽게 변경할 수 있습니다.

아래 함수는 바이트를 KB, MB 또는 GB로 변환한 뒤 형식이 지정된 문자열을 반환합니다. 호출하는 규칙은 $NF(마지막 필드)를 전달하고 반환된 값을 출력하기만 하면 됩니다.

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

출력 전에 배열에 데이터 누적하기

실제 보고서에서는 합계, 평균 또는 순위가 필요한 경우가 많습니다. 이러한 값은 모든 입력을 확인하기 전에는 계산할 수 없습니다. 이때 사용하는 패턴은 다음과 같습니다.

  • 레코드별 처리 단계에서 연관 배열에 데이터를 누적합니다.
  • END에서 파생 값(평균, 백분율)을 계산합니다.
  • 서식이 지정된 출력은 END에서만 생성하여 전체 데이터 집합에 맞게 너비가 조정되도록 합니다.

이처럼 출력을 지연하는 패턴은 awk로 보고서를 생성할 때 사용하는 가장 강력한 관용적 작성법 중 하나입니다. 아래 예제에서는 액세스 로그에서 HTTP 상태 코드별 요청 수와 응답 바이트 수를 집계합니다.

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

awk의 재귀 함수

awk는 재귀를 지원합니다. 대표적인 사용 사례로 팩토리얼 계산이나 반복되는 문자열 생성(예: 터미널 너비에 맞춘 대시 문자열)이 있습니다. 재귀는 범용 언어에 비해 awk에서 덜 일반적이지만, 올바르게 작동하며 주요 구현에서 모두 사용할 수 있습니다.

아래 예제에서는 재귀적인 repeat(s, n) 함수를 정의하고, 가장 너비가 넓은 데이터 행에 자동으로 맞는 구분선을 그리는 데 사용합니다. 하드코딩한 대시는 필요하지 않습니다.

  • 큰 입력에 대해 깊은 재귀를 사용하지 마십시오. awk에는 꼬리 호출 최적화가 없습니다.
  • 단순히 반복하려면 sprintf("%*s", n, "")와 gsub(/ /, "-")를 함께 사용하는 편이 더 빠르지만, 설명용으로는 덜 적합합니다.
#!/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

awk 내부에서 파일 또는 파이프라인으로 printf 사용하기

awk의 printf(및 print)는 awk 프로그램 내부에서 직접 출력을 파일로 리디렉션하거나 셸 명령으로 파이프할 수 있습니다. 전체 작업을 서브셸로 감쌀 필요가 없습니다.

  • printf "..." > "file.txt" — 파일에 씁니다(한 번 잘라낸 후 같은 실행 중에는 이어 씁니다).
  • printf "..." >> "file.txt" — 파일에 내용을 추가합니다.
  • printf "..." | "sort -rn" — 셸 명령으로 파이프합니다. awk는 레코드가 처리되는 동안 파이프를 열어 두었다가 프로그램이 끝날 때 닫습니다.

일반적인 패턴은 로그를 한 번만 읽으면서 상태별 또는 호스트별로 여러 출력 파일에 나누어 쓰는 것입니다. 이렇게 하면 큰 파일을 반복해서 검사하지 않아도 됩니다.

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

awk로 CSV 보고서 생성하기

모든 보고서가 사람이 읽기 위한 것은 아닙니다. 출력이 스프레드시트나 후속 파이프라인으로 전달되는, 기계가 읽을 수 있는 CSV여야 하는 경우도 있습니다. awk의 printf를 사용하면 이를 깔끔하게 처리할 수 있습니다. OFS를 쉼표로 설정하거나 모든 구분 기호를 명시적으로 제어하면 됩니다.

awk에서 견고한 CSV를 생성하기 위한 중요한 규칙은 다음과 같습니다.

  • 쉼표나 줄 바꿈이 포함될 수 있는 필드는 큰따옴표로 감쌉니다.
  • 필드 내부의 리터럴 큰따옴표는 두 개로 늘려 이스케이프합니다: He said ""hello"".

아래의 csv_field(s) 함수는 이 인용 처리를 캡슐화하므로 모든 문자열 필드에 일관되게 적용할 수 있습니다.

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

처음부터 끝까지: 완전한 액세스 로그 보고서

이 마지막 예제에서는 사용자 함수, printf 서식 지정, 배열 누적, 구조화된 머리말과 꼬리말을 모두 함께 사용합니다. 입력은 단순화한 웹 액세스 로그이고, 출력은 메서드별 합계와 총합 행을 포함한 사람이 읽기 쉬운 요약 표입니다.

사용된 핵심 기법은 다음과 같습니다.

  • count[method]++와 bytes[method] += size를 사용하여 한 번의 처리로 메서드별 통계를 누적합니다.
  • human_bytes()(이전 장면에서 다룸)는 바이트 합계를 변환합니다.
  • 머리말, 데이터, 꼬리말에서 너비를 일치시킨 printf를 사용하면 입력 크기와 관계없이 표의 정렬이 유지됩니다.
  • END 블록은 for (k in arr)로 배열을 순회하고, 파이프를 통해 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)
}
'

지식 확인: awk 함수의 지역 변수

POSIX 규칙에 따라 다음 중 awk 함수 내부에서 result를 지역 변수로 올바르게 선언하는 방법은 무엇입니까?

학습 내용 정리: awk 함수, printf 및 보고서 생성

이번 학습에서는 원시 데이터 스트림에서 서식이 지정된 보고서를 생성하는 실무 수준의 awk 프로그램을 작성하는 방법을 배웠습니다. 다음 내용을 기억해 두십시오.

  • 사용자 함수는 function name(params, locals) 형식으로 선언합니다. 지역 변수 매개변수 앞에 공백을 추가하는 방식은 POSIX 규칙이며, local 키워드는 없습니다.
  • printf를 사용하면 출력 너비(%10s), 정렬(%-10s), 정밀도(%8.2f)를 정밀하게 제어할 수 있습니다. \n은 명시적으로 추가해야 합니다.
  • 머리말과 구분선은 BEGIN에서 출력하고, 데이터를 누적하는 작업은 레코드별 규칙에서 수행하며, 합계를 계산하고 완성된 보고서를 출력하는 작업은 END에서 수행하십시오.
  • awk 내부에서 파일(> file) 또는 파이프라인(| "sort")으로 printf 출력을 리디렉션하여 보고서를 나누거나 출력을 후처리할 수 있습니다.
  • CSV를 출력할 때 문자열 필드를 csv_field() 도우미 함수로 감싸면 필드 내부의 쉼표와 따옴표를 안전하게 처리할 수 있습니다.
  • 단위를 변환하는 함수(human_bytes), 문자를 반복하는 함수(repeat), 문자열을 정리하는 함수(trim, csv_field)는 개인 awk 라이브러리에 보관할 가치가 있습니다. 이러한 함수는 여러 프로젝트에서 깔끔하게 조합해 사용할 수 있습니다.

자주 묻는 질문

“awk 함수, printf 서식 지정 및 보고서 생성” 강의는 무료인가요?

네 — “awk 함수, printf 서식 지정 및 보고서 생성” 전체 내용을 이 웹사이트에서 무료로 읽을 수 있습니다. 인터랙티브하게 실습하려면(내장 코드 에디터와 24/7 AI 튜터), CoddyKit PRO로 업그레이드하면 Linux Command Line & Bash Scripting Mastery 강의 전체를 잠금 해제할 수 있습니다. Linux Command Line & Bash Scripting Mastery 강의에는 총 4개의 강의가 포함되어 있습니다.

“awk 함수, printf 서식 지정 및 보고서 생성”에서 뭘 배우나요?

사용자 함수를 정의하고 printf를 사용하여 원시 데이터 스트림에서 보기 좋은 표 형식의 보고서를 출력합니다. 브라우저에서 직접 실행하는 실습 코드로 Linux Command Line & Bash Scripting Mastery을(를) 배우며, 24/7 AI 튜터가 강의를 진행하면서 질문에 답변해줍니다.

Linux Command Line & Bash Scripting Mastery을(를) 시작하는 데 경험이 필요한가요?

사전 경험은 필요하지 않습니다. CoddyKit의 Linux Command Line & Bash Scripting Mastery은(는) 초급자부터 고급 학습자까지를 위해 구성되어 있으므로, 여기서 시작하거나 처음부터 시작할 수 있으며 자신의 속도대로 진행할 수 있습니다. 이것은 4개 중 4번째 강의입니다.

“awk 함수, printf 서식 지정 및 보고서 생성” 강의는 얼마나 걸리나요?

대부분의 CoddyKit 강의는 약 5~10분이 소요됩니다. 각 강의는 간결하고 인터랙티브하여 꾸준한 진행이 가능하며, 웹과 앱에서 중단한 부분부터 바로 시작할 수 있습니다.

이 Linux Command Line & Bash Scripting Mastery 강의에서 코드를 작성하고 실행할 수 있나요?

네. 모든 Linux Command Line & Bash Scripting Mastery 강의에는 내장 코드 에디터가 포함되어 있으므로, 브라우저에서 바로 실제 코드를 작성하고 실행한 후 즉시 AI 피드백을 받을 수 있습니다 — 로컬 설정이 필요 없습니다.

이 강의의 모든 강의

  1. awk의 레코드, 필드 및 사용자 지정 구분자
  2. 패턴, 범위 및 BEGIN/END 블록
  3. awk 배열과 그룹화를 활용한 집계
  4. awk 함수, printf 서식 지정 및 보고서 생성
← Linux Command Line & Bash Scripting Mastery(으)로 돌아가기