-->

리눅스 awk 명령어: 텍스트 처리 마스터를 위한 완벽 가이드

리눅스 전문가의 필수 도구, awk 명령어 완벽 가이드! 기본 문법, 사용법부터 실전 예제, 데이터 처리 팁까지. 로그 분석, 텍스트 가공 능력을 향상시키세요.
인프라코디

리눅스 시스템 관리자, 데브옵스 엔지니어, 혹은 데이터를 다루는 개발자라면 누구나 매일 방대한 양의 텍스트 데이터와 마주하게 됩니다. 로그 파일, CSV 데이터, 시스템 명령어의 출력 결과 등 정형 혹은 비정형 텍스트 속에서 원하는 정보를 추출하고, 가공하며, 의미 있는 보고서를 만드는 작업은 필수적입니다. 이때 grep이나 sed 같은 훌륭한 도구들이 떠오르지만, 데이터의 '열(column)'을 인식하고 계산하며, 프로그래밍적인 제어가 필요할 때 이들의 한계에 부딪히곤 합니다.

바로 이 지점에서 awk가 등장합니다. awk는 단순한 텍스트 필터링 도구를 넘어, 그 자체로 완결된 프로그래밍 언어입니다. 이 글에서는 awk가 왜 수십 년이 지난 지금도 모든 리눅스 전문가의 '비밀 병기'로 불리는지, 그 강력한 작동 원리부터 실무에서 바로 활용할 수 있는 고급 예제까지 모든 것을 상세히 파헤쳐 보겠습니다.

awk, 왜 아직도 모든 리눅스 전문가의 필수 도구일까?

awk는 1977년에 탄생한 오래된 도구이지만, 그 유연성과 강력함 덕분에 클라우드와 빅데이터 시대인 오늘날에도 여전히 그 가치를 인정받고 있습니다. awk의 진정한 힘을 이해하기 위해 그 본질과 역사, 그리고 다른 도구와의 차이점을 먼저 살펴보겠습니다.

awk 소개: 단순한 필터링을 넘어선 데이터 처리 언어

awk를 처음 접하는 사람들은 종종 grep과 유사한 텍스트 검색 도구로 오해하지만, 이는 awk의 극히 일부 기능에 불과합니다. awk의 핵심 정체성은 '데이터 처리 및 리포트 생성 언어'에 있습니다.

정리하자면, awk는 입력된 텍스트(파일 또는 파이프)를 한 줄씩 읽어들여, 정해진 규칙(구분자)에 따라 여러 개의 '필드(field)'로 분리합니다. 그리고 사용자가 정의한 '패턴(pattern)'을 검사하여 조건에 맞는 줄을 찾은 뒤, 해당 줄의 필드들을 가지고 다양한 '액션(action)'을 수행합니다. 이 액션에는 필드 출력, 산술 연산, 문자열 조작, 조건문, 반복문 등 프로그래밍 언어가 갖는 거의 모든 요소가 포함됩니다.

이러한 특징 때문에 awk는 종종 '리눅스의 작은 SQL'에 비유되기도 합니다. SQL이 데이터베이스 테이블에서 특정 조건에 맞는 행과 열을 선택하고, 집계 함수를 이용해 통계를 내는 것처럼, awk는 텍스트 파일이라는 '테이블'에서 특정 패턴을 만족하는 '행(레코드)'을 찾아 그 안의 '열(필드)' 데이터를 자유자재로 가공하고 요약할 수 있습니다. 그래서 많은 전문가들은 awk를 단순한 명령어가 아닌, 텍스트 조작에 특화된 경량 스크립트 언어로 간주합니다.

awk의 탄생: Aho, Weinberger, Kernighan의 유산

awk라는 독특한 이름은 그 기능과 아무런 관련이 없습니다. 이 이름은 1977년, 미국 AT&T 벨 연구소에서 이 도구를 처음 개발한 세 명의 전설적인 개발자, Alfred Aho, Peter Weinberger, Brian Kernighan의 성 앞 글자를 따서 만들어졌습니다.

여기서 특히 주목할 인물은 브라이언 커니핸(Brian Kernighan)입니다. 그는 데니스 리치와 함께 현대 프로그래밍 언어의 근간이 된 C언어의 교과서, "The C Programming Language"를 저술한 인물로 유명합니다. 이 사실은 awk를 이해하는 데 중요한 단서를 제공합니다. awk의 문법을 자세히 들여다보면 C언어와 놀랍도록 닮아있다는 것을 발견할 수 있습니다. 예를 들어, 포맷팅된 출력을 위한 printf 함수, forwhile 반복문, if-else 조건문, 변수 할당 방식 등은 C언어의 문법과 거의 동일합니다.

이는 우연이 아닙니다. awk는 설계 단계부터 C언어의 강력하고 간결한 표현력을 텍스트 처리 영역에 접목하려는 철학을 담고 있었습니다. 덕분에 C, Java, Python 등 C 계열 언어에 익숙한 개발자들은 awk를 매우 쉽게 배울 수 있습니다. awk는 낯선 외계어가 아니라, 이미 익숙한 프로그래밍 패러다임을 셸 환경에서 데이터 처리에 특화시킨 강력한 도구인 셈입니다.

오리지널 awk 이후, 기능이 확장된 nawk(New AWK)나 오늘날 대부분의 리눅스 배포판에서 기본으로 사용하는 GNU 프로젝트 버전인 gawk(GNU AWK) 등 다양한 구현체가 등장하며 발전을 거듭해왔습니다.

grep, sed와는 무엇이 다른가? (핵심 비교)

시스템 관리자들은 종종 grep, sed, awk를 텍스트 처리 삼총사로 부릅니다. 하지만 이들은 각기 다른 목적과 강점을 가지고 있어, 상황에 맞는 도구를 선택하는 것이 매우 중요합니다.

  • grep (Global Regular Expression Print): grep의 핵심 목적은 패턴 검색 및 행 필터링입니다. 파일이나 입력 스트림에서 특정 정규표현식 패턴을 포함하는 '행(line)'을 찾아 그대로 출력하는 역할에 특화되어 있습니다. "로그 파일에서 'ERROR'라는 단어가 포함된 모든 줄을 찾아줘"와 같은 작업에 최적입니다.
  • sed (Stream Editor): sed는 이름 그대로 스트림 편집기입니다. grep처럼 행 단위로 작동하지만, 주로 텍스트를 변환하는 데 사용됩니다. 특정 패턴을 찾아 다른 문자열로 치환(substitution)하거나, 특정 행을 삭제/추가하는 등의 편집 작업을 수행합니다. "파일 내의 모든 'http'를 'https'로 바꿔줘"와 같은 작업에 강력한 힘을 발휘합니다.
  • awk: awkgrepsed의 능력을 포함하면서 한 단계 더 나아갑니다. awk의 핵심은 행을 필드(field) 단위로 분해하여 처리하는 데 있습니다. 각 필드에 대해 산술 연산을 수행하거나, 필드의 값을 조건으로 비교하고, 그 결과를 재구성하여 완전히 새로운 형태의 리포트를 만들 수 있습니다. 변수, 연산자, 제어문 등 프로그래밍 기능을 제공하여 grep이나 sed로는 불가능하거나 매우 복잡한 작업을 간결하게 처리합니다.

이 세 도구의 차이점을 명확히 이해하기 위해 다음 표를 참고하면 좋습니다.

awk vs. grep vs. sed: 언제 어떤 도구를 사용해야 할까?

구분 grep sed awk
핵심 목적 행 필터링 (패턴 검색) 행 편집 (스트림 편집) 행과 열(필드) 기반 데이터 처리 및 리포팅
주요 작업 단위 행 (Line) 행 (Line) 필드 (Field) 및 레코드 (Record)
프로그래밍 기능 없음 제한적 (기본 분기/루프) 강력함 (변수, 연산, 제어문, 함수)
최적 사용 사례 "로그에서 'ERROR' 줄 찾기" "파일에서 'old'를 'new'로 바꾸기" "CSV에서 3번째 열 값이 100 이상인 행의 1, 5열 합산하기"

결론적으로, 단순히 특정 패턴이 포함된 줄을 찾고 싶다면 grep을, 텍스트 내용을 일괄적으로 치환하거나 편집하고 싶다면 sed를, 그리고 데이터를 구조적으로 분석하고, 필드별로 조건을 걸고, 계산을 통해 새로운 정보를 생성하고 싶다면 awk가 정답입니다.

awk의 작동 원리: '패턴-액션' 모델 완벽 해부

awk의 강력함은 그 독특하고 우아한 작동 원리에서 비롯됩니다. awk가 어떻게 데이터를 읽고 처리하는지 이해하면, 복잡해 보이는 문법도 쉽게 정복할 수 있습니다. awk의 세계를 지배하는 두 가지 핵심 개념, '레코드와 필드' 그리고 '패턴-액션 모델'에 대해 알아보겠습니다.

핵심 개념 1: 레코드(Record)와 필드(Field) - 데이터의 기본 단위

awk는 입력 데이터를 구조화된 정보로 바라봅니다. 이때 사용되는 기본 단위가 바로 레코드(Record)필드(Field)입니다.

  • 레코드 (Record): awk가 한 번에 처리하는 데이터의 한 덩어리를 의미합니다. 기본적으로 awk개행 문자(\n)를 레코드 구분자(Record Separator, RS)로 사용하기 때문에, 텍스트 파일의 한 줄(line)이 곧 하나의 레코드가 됩니다. awk는 이 레코드 단위로 입력을 순차적으로 읽어들입니다. awk 스크립트 내에서 현재 처리 중인 레코드 전체 내용은 $0이라는 특수 변수로 참조할 수 있습니다.
  • 필드 (Field): awk는 각 레코드를 더 작은 단위인 필드로 나눕니다. 기본 필드 구분자(Field Separator, FS)는 하나 이상의 공백(space) 또는 탭(tab)입니다. awk는 이 구분자를 기준으로 레코드를 단어 단위로 쪼개고, 첫 번째 필드부터 순서대로 $1, $2, $3,... 와 같은 변수에 자동으로 저장합니다. 현재 레코드가 몇 개의 필드로 이루어져 있는지는 NF(Number of Fields)라는 내장 변수에 저장됩니다.

이 개념을 엑셀 시트에 비유하면 이해가 쉽습니다.

  • 레코드 ($0)는 엑셀의 하나의 행(row)과 같습니다.
  • 필드 ($1, $2,...)는 엑셀의 A열, B열,... 과 같은 각 셀(column)에 해당합니다.
  • NF는 해당 행에 데이터가 들어있는 열의 총 개수를 의미합니다.

awk는 이처럼 모든 텍스트 데이터를 행과 열로 구성된 표(table)처럼 취급하기 때문에, 복잡한 데이터도 체계적으로 다룰 수 있는 것입니다.

핵심 개념 2: pattern { action } 구문의 마법

awk의 모든 동작은 pattern { action } 이라는 단순한 규칙의 조합으로 이루어집니다. awk 스크립트는 이 규칙들의 목록이며, awk는 각 레코드를 읽을 때마다 이 규칙들을 순서대로 적용합니다.

  • pattern (패턴): action을 실행할 조건을 정의하는 부분입니다. 패턴이 참(true)으로 평가되는 레코드에 대해서만 뒤따르는 action이 실행됩니다. 패턴은 매우 다양하게 지정할 수 있습니다.
    • 정규표현식: /ERROR/ 처럼 슬래시(/)로 감싸 특정 문자열 패턴을 찾습니다.
    • 비교 표현식: $3 > 100 처럼 특정 필드의 값을 비교합니다.
    • 논리 표현식: $1 == "admin" && $3 > 0 처럼 AND(&&), OR(||), NOT(!)을 사용해 여러 조건을 조합합니다.
    • 특수 패턴: BEGIN, END (아래에서 설명)
  • action (액션): 패턴이 일치했을 때 수행할 작업을 정의하는 부분입니다. 액션은 항상 중괄호({ })로 감싸야 하며, 세미콜론(;)으로 여러 명령을 나열할 수 있습니다.
    • 출력: print $1, $3 또는 printf "%-10s %d\n", $1, $3
    • 계산: sum += $4
    • 변수 할당: max = $2
    • 제어문: if, for, while

pattern { action } 구조에는 몇 가지 중요한 생략 규칙이 있습니다.

  • pattern이 생략되면: { action }만 남게 되며, 이 경우 모든 레코드에 대해 해당 action이 실행됩니다.
  • action이 생략되면: pattern만 남게 되며, 이 경우 패턴과 일치하는 레코드 전체를 출력하는 기본 액션, 즉 { print $0 }이 자동으로 실행됩니다.

pattern { action } 모델은 awk가 매우 선언적(Declarative)이고 데이터-주도적(Data-Driven)인 프로그래밍 패러다임을 따르게 합니다. 일반적인 절차적 프로그래밍에서는 "파일을 열고, 한 줄씩 읽는 루프를 만들고, 루프 안에서 if문으로 조건을 검사한 뒤, 작업을 수행하라"는 식으로 모든 절차를 명시적으로 코딩해야 합니다. 하지만 awk에서는 "이런 패턴의 데이터가 오면, 이런 액션을 하라"는 규칙만 선언하면 됩니다. 데이터를 읽고, 루프를 돌고, 조건을 검사하는 복잡한 내부 메커니즘은 awk 엔진이 알아서 처리해줍니다. 개발자는 데이터 처리의 '무엇(what)'에만 집중하고, '어떻게(how)'는 awk에 위임하는 것입니다. 이것이 바로 awk 스크립트가 놀랍도록 간결하고 강력한 이유입니다.

awk의 처리 흐름: BEGIN과 END 블록의 전략적 활용

awk는 단순히 레코드를 순차적으로 처리하는 것을 넘어, 전체 프로세스를 '시작-중간-끝'의 3단계로 나누어 제어할 수 있는 강력한 기능을 제공합니다. 이는 특수 패턴인 BEGINEND 블록을 통해 구현됩니다.

  1. BEGIN 블록 (준비 단계):
    BEGIN 패턴에 연결된 액션은 awk입력 파일을 읽기 시작하기 전, 단 한 번만 실행됩니다. 주로 다음과 같은 준비 작업에 사용됩니다.
    • 결과 보고서의 헤더(머리글) 출력
    • 합계나 카운트를 저장할 변수 초기화 (예: sum = 0)
    • 필드 구분자(FS)와 같은 내장 변수 값 설정
  2. pattern { action } 블록 (실행 단계):
    이것이 awk의 주된 작업 영역입니다. awk는 입력의 각 레코드(줄)에 대해 여기에 정의된 패턴-액션 규칙들을 순서대로 적용하며 반복 실행합니다.
  3. END 블록 (마무리 단계):
    END 패턴에 연결된 액션은 awk모든 레코드 처리를 마친 후, 단 한 번만 실행됩니다. 주로 다음과 같은 마무리 작업에 사용됩니다.
    • 누적된 변수를 이용한 최종 결과 계산 및 출력 (총합, 평균 등)
    • 보고서의 꼬리말(요약) 출력

이 3단계 처리 모델을 '보고서 작성'에 비유할 수 있습니다. BEGIN은 보고서의 제목과 표의 열 이름을 인쇄하는 단계, 메인 블록은 데이터 한 줄 한 줄을 읽어 표의 본문을 채워나가는 단계, 그리고 END는 모든 데이터 처리가 끝난 뒤 표 아래에 총계와 평균 같은 요약 정보를 추가하는 단계입니다. 이 구조를 잘 활용하면 매우 체계적이고 전문적인 데이터 리포트를 손쉽게 생성할 수 있습니다.

awk 명령어 기본 문법 및 핵심 구성 요소

이제 awk의 작동 원리를 이해했으니, 실제로 명령어를 사용하는 데 필요한 구체적인 문법과 도구들을 살펴보겠습니다. awk를 자유자재로 다루기 위해 반드시 알아야 할 기본 구문, 주요 옵션, 그리고 핵심 내장 변수들을 정리했습니다.

기본 구문: awk [옵션] '프로그램' [파일]

awk 명령어의 가장 기본적인 형태는 다음과 같습니다.

awk [옵션] '패턴1 { 액션1 } 패턴2 { 액션2 }...' [입력_파일]
  • [옵션]: -F, -f, -vawk의 동작을 제어하는 옵션입니다.
  • '프로그램': 작은따옴표(' ') 안에 pattern { action } 규칙들을 작성합니다. 셸이 특수문자를 해석하는 것을 막기 위해 작은따옴표로 감싸는 것이 일반적입니다.
  • [입력_파일]: 처리할 대상 파일의 경로를 지정합니다. 만약 파일명을 생략하면, awk는 파이프(|)를 통해 전달되는 표준 입력(standard input)을 데이터로 사용합니다.

awk 주요 옵션

awk의 활용 범위를 극적으로 넓혀주는 세 가지 핵심 옵션이 있습니다. 이 옵션들을 마스터하는 것이 중급 사용자로 가는 지름길입니다.

awk 핵심 커맨드라인 옵션

옵션 설명 사용 예시
-F 구분자 입력 필드 구분자(내장 변수 FS)를 지정합니다. 기본값인 공백 대신 특정 문자를 기준으로 필드를 나눌 때 사용합니다. CSV 파일의 콤마(,), /etc/passwd 파일의 콜론(:) 등을 처리할 때 필수적입니다. awk -F':' '{print $1}' /etc/passwd
-f 스크립트파일 awk 프로그램(패턴-액션 규칙)을 명령행에 직접 쓰는 대신, 별도의 파일에 저장하고 불러와 실행합니다. 복잡한 로직을 재사용하거나 체계적으로 관리할 때 매우 유용합니다. awk -f my_script.awk data.log
-v 변수=값 awk 스크립트 외부의 셸 변수 값을 awk 내부의 변수로 전달합니다. 이를 통해 스크립트의 유연성을 높이고 동적으로 제어할 수 있습니다. limit=100; awk -v threshold="$limit" '$3 > threshold' data.txt

-F 옵션은 다양한 데이터 포맷의 제약을 없애주고, -f 옵션은 일회성 명령어를 재사용 가능한 스크립트로 격상시켜주며, -v 옵션은 셸 환경과 awk 스크립트를 통합하는 강력한 다리 역할을 합니다.

awk 핵심 내장 변수

awk는 스크립트 내에서 데이터 스트림의 상태를 파악하고 제어할 수 있도록 다양한 내장 변수(Built-in Variables)를 제공합니다. 이 변수들은 awk 프로그램이 데이터와 상호작용하는 일종의 'API'와 같습니다. 다음은 반드시 알아야 할 필수 내장 변수들입니다.

반드시 알아야 할 awk 내장 변수

변수명 설명
NR Number of Records. 현재까지 처리한 총 레코드(행)의 누적 번호입니다.
NF Number of Fields. 현재 처리 중인 레코드의 필드(열) 개수입니다.
FS Field Separator. 입력 필드를 구분하는 문자입니다. 기본값은 공백/탭입니다.
OFS Output Field Separator. print문으로 여러 필드를 출력할 때 필드 사이에 들어갈 구분자입니다. 기본값은 공백입니다.
RS Record Separator. 입력 레코드를 구분하는 문자입니다. 기본값은 개행(\n)입니다.
ORS Output Record Separator. print문이 실행될 때 레코드 끝에 추가될 구분자입니다. 기본값은 개행(\n)입니다.
FILENAME 현재 처리 중인 입력 파일의 이름입니다.
$0 현재 처리 중인 레코드(행)의 전체 내용입니다.
$n n번째 필드의 내용입니다 ($1, $2, $NF 등).

이 변수들을 활용하면 NR로 특정 줄 번호의 데이터만 처리하거나, NF로 필드 개수가 다른 비정형 데이터를 걸러내고, FSOFS를 조작하여 입출력 형식을 자유자재로 제어하는 등 정교한 데이터 가공이 가능해집니다.

awk 실전 활용 예제: 초급부터 고급까지

이론 학습을 마쳤으니, 이제 awk를 실제 문제 해결에 적용해볼 시간입니다. 초급부터 고급까지 난이도별 실전 예제를 통해 awk의 강력한 기능을 단계적으로 체득해 보겠습니다. 모든 예제는 가상의 데이터 파일 data.txtaccess.log를 기반으로 설명합니다.

샘플 파일: employee.txt

ID Name Department Role Salary JoinDate
101 Alice Sales Manager 85000 2020-03-15
102 Bob Engineering Developer 92000 2019-07-22
103 Charlie Engineering DevOps 105000 2021-01-10
104 David Sales Analyst 76000 2022-05-30
105 Eve Marketing Specialist 81000 2020-11-01

샘플 파일: access.log

192.168.1.10 - - [10/Oct/2023:10:00:01 +0900] "GET /index.html HTTP/1.1" 200 1543
172.16.0.5 - - [10/Oct/2023:10:00:02 +0900] "GET /api/users HTTP/1.1" 200 876
192.168.1.10 - - [10/Oct/2023:10:00:03 +0900] "POST /login HTTP/1.1" 401 128
210.100.20.3 - - [10/Oct/2023:10:00:04 +0900] "GET /images/logo.png HTTP/1.1" 200 5620
172.16.0.5 - - [10/Oct/2023:10:00:05 +0900] "GET /api/products HTTP/1.1" 404 50

기본기 다지기: 선택과 출력

특정 필드 출력하기 (print $n)

가장 기본적인 작업은 원하는 열(필드)의 데이터만 선택하여 출력하는 것입니다.

  • 요구사항: 모든 직원의 이름(2번째 필드)과 연봉(5번째 필드)을 출력하세요.
  • 명령어:
    awk '{print $2, $5}' employee.txt
  • 실행 결과:
    Name Salary
    Alice 85000
    Bob 92000
    Charlie 105000
    David 76000
    Eve 81000
  • 설명: awkemployee.txt를 한 줄씩 읽어 공백을 기준으로 필드를 나눈 뒤, 각 줄의 2번째 필드($2)와 5번째 필드($5)를 출력합니다. print문에서 콤마(,)는 기본 출력 필드 구분자(OFS), 즉 공백 한 칸으로 변환됩니다.

원하는 문자열(패턴)을 포함한 행 필터링

정규표현식을 사용하여 특정 패턴이 포함된 행만 골라낼 수 있습니다.

  • 요구사항: 'Engineering' 부서에 속한 직원들의 정보만 출력하세요.
  • 명령어:
    awk '/Engineering/' employee.txt
  • 실행 결과:
    102 Bob Engineering Developer 92000 2019-07-22
    103 Charlie Engineering DevOps 105000 2021-01-10
  • 설명: /Engineering/ 이라는 패턴을 사용했습니다. awk는 각 행에 'Engineering'이라는 문자열이 포함되어 있는지 검사하고, 포함된 행만 기본 액션인 print $0(전체 행 출력)을 실행합니다.

출력 형식 꾸미기 (printf 활용)

print는 간편하지만, 출력 형식을 세밀하게 제어하고 싶을 때는 C언어 스타일의 printf 함수를 사용해야 합니다.

  • 요구사항: 직원들의 이름과 연봉을 깔끔하게 정렬된 표 형식으로 출력하세요. 이름은 왼쪽 정렬로 10칸, 연봉은 오른쪽 정렬로 8칸을 할당합니다.
  • 명령어:
    awk 'NR > 1 {printf "%-10s | $%8d\n", $2, $5}' employee.txt
  • 실행 결과:
    Alice | $   85000
    Bob | $   92000
    Charlie | $  105000
    David | $   76000
    Eve | $   81000
  • 설명:
    • NR > 1: 첫 번째 줄(헤더)은 건너뛰기 위한 패턴입니다.
    • printf "포맷", 변수1, 변수2: printf의 기본 구조입니다.
    • %-10s: -는 왼쪽 정렬, 10은 총 10칸 확보, s는 문자열(string)을 의미합니다.
    • %8d: 총 8칸을 확보하여 10진수 정수(decimal integer)를 오른쪽 정렬(기본값)로 출력합니다.
    • \n: printfprint와 달리 자동 줄바꿈을 하지 않으므로, 명시적으로 개행 문자를 넣어줘야 합니다.

중급 활용: 연산과 조건

숫자 필드로 산술 연산하기 (합계, 평균)

awk의 진정한 힘은 필드를 숫자로 인식하고 계산할 수 있다는 점입니다. END 블록과 함께 사용하면 강력한 요약 리포트를 만들 수 있습니다.

  • 요구사항: 모든 직원의 연봉 총합과 평균 연봉을 계산하여 출력하세요.
  • 명령어:
    awk 'NR > 1 { sum += $5; count++ } END { print "Total Salary:", sum; print "Average Salary:", sum/count }' employee.txt
  • 실행 결과:
    Total Salary: 439000
    Average Salary: 87800
  • 설명:
    • NR > 1: 헤더를 제외한 데이터 행에 대해서만 액션을 수행합니다.
    • { sum += $5; count++ }: 각 데이터 행을 처리할 때마다 sum 변수에 5번째 필드(연봉) 값을 누적하고, count 변수를 1씩 증가시킵니다. sumcount는 사용자가 정의한 변수입니다.
    • END {... }: 모든 행 처리가 끝난 후, END 블록이 실행됩니다. 누적된 sumcount를 이용해 총합과 평균을 계산하고 출력합니다.

조건문(if)을 이용한 정교한 필터링

패턴 매칭만으로 부족할 때, if문을 사용하면 더 복잡하고 정교한 조건 필터링이 가능합니다.

  • 요구사항: 연봉이 90000 이상인 'Engineering' 부서 직원들의 이름과 역할을 출력하세요.
  • 명령어:
    awk '$3 == "Engineering" && $5 >= 90000 { print $2, $4 }' employee.txt
  • 실행 결과:
    Bob Developer
    Charlie DevOps
  • 설명: 이 예제는 비교 표현식을 패턴으로 사용했습니다. if문을 명시적으로 쓸 수도 있습니다: awk '{ if ($3 == "Engineering" && $5 >= 90000) { print $2, $4 } }' employee.txt. 결과는 동일합니다. awk에서는 간단한 조건의 경우, 패턴 영역에 직접 조건을 쓰는 것이 더 간결합니다.

패턴 매칭 연산자(~, !~)와 정규표현식

특정 필드의 내용이 정규표현식과 일치하는지 검사하려면 물결표(~) 연산자를 사용합니다.

  • 요구사항: 역할(Role)이 'dev' 또는 'Dev'로 시작하는(대소문자 무시) 직원의 이름과 역할을 출력하세요.
  • 명령어:
    awk '$4 ~ /^ev/ { print $2, $4 }' employee.txt
  • 실행 결과:
    Bob Developer
    Charlie DevOps
  • 설명:
    • $4 ~ /.../: 4번째 필드의 값이 뒤따르는 정규표현식과 매치되는지 검사합니다.
    • /^ev/: ^는 문자열의 시작을 의미, 는 'D' 또는 'd'를 의미, ev는 'ev' 문자열을 의미합니다. 즉, 'Dev' 또는 'dev'로 시작하는 패턴입니다.
    • 반대로 일치하지 않는 경우를 찾으려면 !~ 연산자를 사용합니다.

고급 테크닉: 프로그래밍처럼 사용하기

반복문(for, while)으로 다중 필드 처리

for 루프를 사용하면 레코드의 모든 필드를 순회하며 동적인 처리를 할 수 있습니다.

  • 요구사항: 각 직원의 모든 정보를 한 줄에 하나씩 출력하세요.
  • 명령어:
    awk '{ for (i=1; i<=NF; i++) { print "Field " i ": " $i }; print "---" }' employee.txt
  • 실행 결과 (일부):
    Field 1: ID
    Field 2: Name
    ...
    ---
    Field 1: 101
    Field 2: Alice
    ...
    ---
  • 설명: for (i=1; i<=NF; i++) 구문은 현재 레코드의 첫 번째 필드부터 마지막 필드(NF)까지 순회합니다. 루프 안에서 i는 필드 번호가 되고, $i를 통해 해당 필드의 값에 접근할 수 있습니다.

사용자 정의 변수로 스크립트 확장하기

앞서 합계 예제에서 봤듯이, awk는 사용자 정의 변수를 자유롭게 선언하고 사용할 수 있습니다.

  • 요구사항: access.log 파일에서 404 에러(Not Found)가 몇 번 발생했는지 카운트하세요.
  • 명령어:
    awk '$9 == 404 { error_count++ } END { print "404 Not Found Errors:", error_count }' access.log
  • 실행 결과:
    404 Not Found Errors: 1
  • 설명: 9번째 필드(HTTP 상태 코드)가 404일 때마다 error_count라는 변수를 1씩 증가시킵니다. 모든 처리가 끝난 후 END 블록에서 최종 카운트를 출력합니다.

연관 배열(Associative Array)을 활용한 데이터 집계

awk의 가장 강력한 기능 중 하나는 연관 배열입니다. 다른 언어의 해시맵(HashMap)이나 딕셔너리(Dictionary)처럼, 문자열을 인덱스(키)로 사용하여 값을 저장할 수 있습니다. 이를 이용하면 그룹별 통계를 매우 쉽게 낼 수 있습니다.

  • 요구사항: access.log에서 각 IP 주소별로 접속 횟수를 집계하여 출력하세요.
  • 명령어:
    awk '{ ip_counts[$1]++ } END { for (ip in ip_counts) { print ip, ip_counts[ip] } }' access.log
  • 실행 결과:
    192.168.1.10 2
    172.16.0.5 2
    210.100.20.3 1
  • 설명:
    • ip_counts[$1]++: 이 한 줄이 마법의 핵심입니다. ip_counts라는 연관 배열에 1번째 필드(IP 주소)를 키로 사용합니다. 해당 IP가 처음 나타나면 ip_counts['192.168.1.10'] 항목이 생성되고 값이 1이 됩니다. 다시 나타나면 기존 값에 1이 더해집니다.
    • END { for (ip in ip_counts)... }: 모든 로그 처리가 끝나면, for...in 루프를 사용해 ip_counts 배열의 모든 키(IP 주소)를 순회하며 키와 해당 값을 출력합니다.

다른 명령어와 파이프(|)로 연동하기

awk는 단독으로도 강력하지만, 다른 리눅스 명령어와 파이프(|)로 연결될 때 그 진가가 드러납니다. 명령어의 출력 결과를 실시간으로 받아 분석하고 요약할 수 있습니다.

  • 요구사항: 현재 실행 중인 모든 프로세스가 사용 중인 메모리(RSS, 6번째 필드)의 총합을 MB 단위로 계산하세요.
  • 명령어:
    ps aux | awk 'NR > 1 { sum += $6 } END { printf "Total RSS: %.2f MB\n", sum/1024 }'
  • 실행 결과 (예시):
    Total RSS: 1234.56 MB
  • 설명: ps aux 명령어의 출력 결과를 파이프로 awk에 전달합니다. awk는 헤더(첫 번째 줄)를 제외한 각 프로세스 정보에서 6번째 필드(RSS, KB 단위)를 sum 변수에 누적합니다. 모든 프로세스 정보 처리가 끝나면 END 블록에서 총합을 1024로 나누어 MB 단위로 변환하고 printf로 소수점 둘째 자리까지 출력합니다.

awk 스크립트 작성 및 고급 팁

awk에 익숙해지면 한 줄짜리 명령어를 넘어, 복잡한 로직을 담은 스크립트를 작성하고 싶어집니다. awk를 일회성 명령이 아닌 재사용 가능한 도구로 관리하는 방법과 현업에서 마주칠 수 있는 문제 해결 팁을 소개합니다.

-f 옵션으로 awk 스크립트 파일 실행하기

명령어가 길어지거나 로직이 복잡해지면, 셸 명령행에 직접 입력하는 것은 비효율적이고 가독성도 떨어집니다. 이럴 때 -f 옵션을 사용하면 awk 코드를 별도의 파일로 분리하여 관리할 수 있습니다.

1. awk 스크립트 파일 작성 (report.awk)

# report.awk - 직원 연봉 리포트 생성 스크립트

# 처리 시작 전 헤더 출력
BEGIN {
    FS = " "  # 입력 필드 구분자 (명시적으로 지정)
    OFS = "\t" # 출력 필드 구분자는 탭으로 설정
    print "Name", "Department", "Salary"
    print "------------------------------------"
}

# 연봉이 80000 이상인 직원 정보만 출력
$5 >= 80000 {
    print $2, $3, $5
}

# 모든 처리 후 요약 정보 출력
END {
    print "------------------------------------"
    print "Report generated on:", strftime("%Y-%m-%d %H:%M:%S")
}

2. -f 옵션으로 스크립트 실행

awk -f report.awk employee.txt

이 방식은 awk 코드를 '관리'하고 '재사용'하는 전문가적인 접근법입니다. 셸 스크립트 안에 길고 복잡한 awk 코드를 인라인으로 넣는 대신, 로직을 별도의 .awk 파일로 분리함으로써 가독성과 유지보수성을 크게 향상시킬 수 있습니다.

팁: 스크립트 파일 상단에 #!/usr/bin/awk -f 와 같은 쉬뱅(shebang) 라인을 추가하고 실행 권한(chmod +x report.awk)을 주면, ./report.awk employee.txt 처럼 독립적인 실행 파일처럼 사용할 수 있습니다.

awk 내에서 셸 명령어 실행하기 (system 함수)

awksystem() 함수를 사용하면 awk 스크립트 실행 도중에 외부 셸 명령어를 호출할 수 있습니다. 이는 awk의 역할을 단순한 '필터'에서 '능동적인 에이전트'로 확장시킵니다.

  • 요구사항: access.log를 분석하여 상태 코드가 401(Unauthorized)인 접근을 시도한 IP를 banned_ips.txt 파일에 추가하세요.
  • 명령어:
    awk '$9 == 401 { system("echo " $1 " >> banned_ips.txt") }' access.log
  • 설명: 9번째 필드가 401인 라인을 찾을 때마다 system() 함수가 호출됩니다. 함수에 전달된 문자열 "echo " $1 " >> banned_ips.txt"는 셸에서 실행될 명령어가 됩니다. $1은 현재 라인의 IP 주소로 치환되어, echo 192.168.1.10 >> banned_ips.txt 와 같은 명령이 실행됩니다. 이처럼 데이터 분석 결과에 따라 파일 시스템을 조작하거나 다른 스크립트를 호출하는 등 능동적인 자동화 작업이 가능해집니다.

자주 발생하는 오류와 해결 팁

awk는 강력한 만큼 처음 사용할 때 몇 가지 흔한 실수를 저지르기 쉽습니다. 자주 발생하는 오류 유형과 해결책을 알아두면 디버깅 시간을 크게 줄일 수 있습니다.

  • 오류 1: 따옴표 지옥 (Quoting Hell)
    셸 변수를 awk 스크립트 안에서 사용할 때 작은따옴표와 큰따옴표의 혼동으로 문제가 자주 발생합니다.
    • 잘못된 예: limit=85000; awk '$5 > $limit' employee.txt
      • awk 프로그램 전체가 작은따옴표(' ')로 묶여있기 때문에, 셸 변수 $limit이 확장되지 않고 문자열 '$limit'으로 취급됩니다.
    • 해결책: -v 옵션을 사용하는 것이 가장 깔끔하고 안전한 방법입니다.
      • 올바른 예: limit=85000; awk -v threshold="$limit" '$5 > threshold' employee.txt
  • 오류 2: 숫자 vs. 문자열 비교 혼동
    awk는 문맥에 따라 변수 타입을 유추하지만, 비교 연산 시 의도치 않은 결과를 낳을 수 있습니다.
    • 예시: awk '$1 > "10"'awk '$1 > 10' 은 다르게 동작할 수 있습니다.
      • "10"(큰따옴표 안)은 문자열로 취급되어 사전 순서로 비교합니다. 이 경우 "2"는 "10"보다 크다고 판단됩니다 (첫 글자 '2'가 '1'보다 크므로).
      • 10(따옴표 없음)은 숫자로 취급되어 수의 크기를 비교합니다.
    • 해결책: 숫자 비교를 의도했다면, 비교 대상에 따옴표를 사용하지 않도록 주의해야 합니다. 필드 값이 확실히 숫자가 되도록 +0을 더해주는 트릭($1+0 > 10)도 유용합니다.
  • 오류 3: END 블록 사용 잊기
    합계나 평균을 계산할 때 END 블록을 사용하지 않아 매 라인마다 중간 결과가 출력되는 것은 초보자들이 흔히 하는 실수입니다.
    • 잘못된 예: awk '{ sum += $5; print sum }' employee.txt
    • 해결책: 값의 누적은 메인 블록에서 수행하고, 최종 결과 출력은 반드시 END 블록 안에서 해야 합니다.
      • 올바른 예: awk '{ sum += $5 } END { print sum }' employee.txt

마무리: awk를 마스터하여 데이터 처리 능력을 한 단계 위로

지금까지 awk의 탄생 배경과 철학부터, 핵심 작동 원리, 그리고 실전 예제를 통한 고급 활용법까지 여정을 함께했습니다. awk는 단순히 오래된 유닉스 명령어가 아니라, pattern { action }이라는 우아하고 강력한 모델을 기반으로, 무질서한 텍스트 데이터 스트림을 구조화된 정보로 변환하는 완결된 프로그래밍 언어임을 확인했습니다.

grep이 텍스트에서 원하는 '줄'을 찾는 돋보기라면, sed는 그 줄을 다듬는 조각칼입니다. 그리고 awk는 데이터라는 원석을 행과 열로 분해하고, 계산하고, 재조합하여 빛나는 보석 같은 '보고서'로 만들어내는 정교한 세공 도구입니다. 연관 배열을 이용한 동적 그룹핑과 집계 능력, 다른 명령어와의 유연한 파이프라인 연동은 awk가 왜 데이터가 넘쳐나는 현대 IT 환경에서도 여전히 대체 불가능한 도구인지를 증명합니다.

오늘 배운 지식을 머릿속에만 두지 마십시오. 지금 바로 여러분의 터미널을 열고, 매일 분석하는 로그 파일이나 데이터에 오늘 배운 간단한 예제부터 적용해 보시길 권장합니다. 처음에는 print $1로 시작하더라도, 곧 if문으로 조건을 걸고, 연관 배열로 통계를 내며, 복잡한 셸 스크립트를 awk 한 줄로 대체하고 있는 자신을 발견하게 될 것입니다.

awk를 마스터하는 것은 단순히 새로운 명령어를 하나 더 배우는 것이 아닙니다. 그것은 데이터를 바라보는 관점을 바꾸고, 복잡한 문제를 해결하는 능력을 한 단계 끌어올리는, 모든 리눅스 전문가를 위한 강력한 여정의 시작입니다.

인프라코디
서버, 네트워크, 보안 등 IT 인프라 관리를 하는 시스템 엔지니어로 일하고 있으며, IT 기술 정보 및 일상 정보를 기록하는 블로그를 운영하고 있습니다. 글을 복사하거나 공유 시 게시하신 글에 출처를 남겨주세요.

- 블로그 : www.infracody.com

이 글이 유익했나요? 댓글로 소중한 의견을 남겨주시거나 커피 한 잔의 선물은 큰 힘이 됩니다.
댓글