클라우드 아키텍트 양성과정

쉘 프로그래밍 awk programming

종바깅 2021. 5. 12. 11:57

awk programming

  • 구분자(Delimiter) : 텍스트 데이터에서 필드(Field)를 구분하는 기호

awk 소개

  • 만든사람의 이름 첫 글자를 모아서 만든이름(Aho, Kernighan, and Weinberger)

  • awk 도구는 유닉스 용으로 만들어진 도구

  • awk는 이후 new awk로 발전

  • gawk : GNU awk = nawk

  • 데이터 조작, 리포트 생성 등을 지원하는 도구

  • awk 명령어 내에서 내부적으로 프로그래밍 기능 지원

  • *레코드 단위의 처리를 수행 - 기본적으로 각 줄을 한 레코드로 처리 *

    → 레코드를 구분하는 구분자 : 개행문자

  • 레코드 내의 데이터는 필드 단위로 처리 가능

  • 정규화 표현식 등을 사용할 수 있음

  • grep, sed등과 사용법이 유사하며, 비슷한 기능을 가지고 있음

awk 용어 정리

  • 레코드 : 한 번에 처리되는 데이터의 단위, 기본적으로 한 줄 단위

  • 필드 : 레코드 내의 데이터를 구분하는 단위, 기본적으로는 공백을 구분자로 사용

  • 레코드 구분자 : RS(Record Separator) - awk 내에서 RS 변수가 레코드 구분자를 저장

  • 필드 구분자 : FS(Field Seperator), awk 내에서 FS 변수가 필드 구분자를 저장

  • ORS : Output RS, 출력 시 레코드 사이에 삽입되는 값, 기본으로 개행, 변수값을 변경하는 것으로 다른 값으로 변경 가능

  • OFS : Output FS, 출력형식 지정시 ',' 기호를 통해 사용가능, 기본으로 공백

  • NF : Number of Field, 레코드를 읽오올 때 필드 구분자에 의해 분리된 필드의 갯수

  • NR : Number of Record, 현재 처리중인 레코드의 번호

  • $0 : 전체 필드 데이터, 각 필드 사이에 OFS를 삽입하여 출력

  • $1~ : 각 위치의 필드

awk 실행 명령

  • awk [옵션] '/패턴' [대상파일] : grep과 동일한 동작

  • awk [옵션] '{명령}' [대상파일] : 대상파일의 각 레코드 별로 명령 실행

  • awk [옵션] '/패턴 {명령}' [대상파일] : 패턴에 해당하는 레코드에 대해 명령 실행

  • awk [옵션] '범위 {명령}' [대상파일] : 범위에 해당하는 레코드에 대해 명령 실행

awk 옵션

  • -F : 구분자 지정
    • 특정 하나의 기호를 구분자로 사용 -F :
    • 여러 기호를 한꺼번에 구분자로 사용 : -F '[구분자 기호집합]'
      • -F'[:,]'
      • -F'[ \t]'

명령

  • print : 데이터를 사용자가 지정한 형식대로 출력
  • printf : prinf + format(출력형식)

print 명령 내에서 사용할 수 있는 출력 형식

  • print는 사용자가 입력한 모양대로 출력을 생성
  • $1, $2 등 필드를 지정하여 출력 가능
  • 임의의 문자열 삽입 : “ “
  • 내부에서 ‘ , ‘ 기호 사용시 자동으로 OFS 삽입
  • 일부 특수한 용도의 문자를 사용할 수 있음
    • \b : 백스페이스, 커서를 앞으로 한 칸 이동
    • \n : new line. 개행
    • \r: Carriage Return. 커서가 라인의 맨 앞으로 이동
    • \a : Beep. 소리 발생
    • \t : Tab 삽입
    • \047 : ‘ 기호, 숫자 변경시 다른 특수문자 출력(코드)

패턴의 다양한 활용법

  • ~ : Match 연산자

    • [필드변수] ~[정규화표현식] : 특정 필드가 정규화 표현식을 만족하는지 확인
    • !과 함꼐 사용 가능 : [필드변수] !~[정규화표현식] : 특정 필드가 정규화 표현식을 만족하지 않을 경우
  • 비교연산자 사용 가능( >, <, >=, <=, ==)

    • ==일 경우 문자열도 가능
  • 논리연산자 사용가능

    • <패턴1> && <패턴2> : 패턴1과 패턴2를 모두 만족하는 경우

    • <패턴2> || <패턴2> : 패턴1과 패턴2중 하나라도 만족하는 경우

      [root@server1 ~]# awk -F: '$1 == "root"' /etc/passwd
      root:x:0:0:root:/root:/bin/bash            //1번필드가 root인 경우
      
      [root@server1 ~]# awk -F: '$1 ~ /root/' /etc/passwd
      root:x:0:0:root:/root:/bin/bash            //1번필드가 root 정규화 표현식을 만족하는 경우
      
      [root@server1 ~]# awk -F: '$1 ~ /^root$/' /etc/passwd
      root:x:0:0:root:/root:/bin/bash            //1번필드가 ^root$ 정규화 표현식을 만족하는 경우

출력식 데이터 변경

  • 사칙연산 : 특정 필드의 값 출력시 사칙연산을 적용하여 출력가능

    • $2+10 : 2번 필드 값 출력시 10을 더하여 출력
    • $2-10 : 2번 필드 값 출력시 10을 빼서 출력
    • $2/10 : 2번 필드 값 출력시 10을 나누어 출력
    • $2*10 : 2번 필드 값 출력시 10을 곱하여 출력
    • $2%10 : 2번 필드 값 출력시 10으로 나눈 나머지를 출력
  • 문자열 값 변경 가능

    • $1=”hello” 1번 필드 값을 hello로 변경하여 출력
  • 가산, 감산

    • $2+=10 : $2=$2+10
    • $2-=10: $2=$2-10
  • 선택적인 데이터 출력

    • '{print(A?B:C)}'

      • A : 조건(참/거짓)
      • B : 조건이 참일 경우 출력할 내용
      • C : 조건이 거짓일 경우 출력할 내용
      //사칙연산
      [root@server1 ~]# awk -F '[:,]' '$2>80 {print $1,$2}' test.txt
      kim 90
      lee 100
      [root@server1 ~]# awk -F '[:,]' '$2>80 {print $1,$2+10}' test.txt
      kim 100
      lee 110
      [root@server1 ~]# awk -F '[:,]' '$2>80 {print $1,$2-10}' test.txt
      kim 80
      lee 90
      [root@server1 ~]# awk -F '[:,]' '$2>80 {print $1,$2/10}' test.txt
      kim 9
      lee 10
      [root@server1 ~]# awk -F '[:,]' '$2>80 {print $1,$2*10}' test.txt
      kim 900
      lee 1000
      [root@server1 ~]# awk -F '[:,]' '$2>80 {print $1,$2%3}' test.txt
      kim 0
      lee 1
      
      //문자열 변경
      [root@server1 ~]# awk -F '[:,]' '$2>80 {print $1="anonimous",$2}' test.txt
      anonimous 90
      anonimous 100
      
      //가산, 감산
      [root@server1 ~]# awk -F '[:,]' '$2>80 {$2+=10; print $1,$2}' test.txt
      kim 100
      lee 110
      [root@server1 ~]# awk -F '[:,]' '$2>80 {$2-=10; print $1,$2}' test.txt
      kim 80
      lee 90
      
      //선택적인 데이터 출력
      [root@server1 ~]# awk -F '[:,]' '{print $1, ($2>80?"Pass":"Not Pass")}' test.txt
      kim Pass
      lee Pass
      park Not Pass

실습문제

1. 전화번호를 모두 출력하세요
[root@server1 ~]# awk -F: '{print $2}' /practice/donors

2. Dan의 전화번호를 출력하세요
[root@server1 ~]# awk -F: '$1~/Dan/ {print $1 $2}' /practice/donors

3. Susan의 성(family name)과 전화번호를 출력하세요
[root@server1 ~]# awk -F'[: ]' '$1 ~ /Susan/ {print $2,$3,$4}' /practice/donors

4. C나 E로 시작하는 이름을 출력하세요
[root@server1 ~]# awk -F: '$1 ~ /^[CE]/ {print $1}' /practice/donors

5. 지역번호가 916인 사람들의 이름을 출력하세요
[root@server1 ~]# awk -F'[: ]' '$3 ~ /916/ {print $1}' /practice/donors 

6. Mike의 기부금을 출력하세요. 액수는 달러 기호($)로 시작해야 합니다 (예: $250, $100)
[root@server1 ~]# awk -F: '$1~/Mike/ {print $1": $"$3",$"$4",$"$5}' /practice/donors

BEGIN, END

변수의 초기화 : 변수를 선언할 때 기본값을 입력

  • awk 에서는 변수를 사영하기가 편리함, 사용하면 즉시 생성되고 초기화 됨

    • 수치일 경우 : 0
    • 문자일 경우 : ""(빈 문자열)
  • BEGIN

    • awk 명령어 시작 시 한 번만 수행되는 내용
    • 블록 형태로 작성
    • 변수의 선언 및 초기화
    • 기본 내장 변수값을 변경할 때 사용
    • 헤더, 타이틀 등을 출력할 때 사용
  • END

    • 모든 레코드에 대한 처리가 완료된 이후 실행(1번만 실행)

    • 결과치를 출력하기 위한 용도로 사용

    • 푸터(footer) 아래쪽에 들어갈 내용 츨력시 사용

        [root@server1 ~]# awk 'BEGIN{FS="[:,]";OFS="\t"; KOR=100; print "KOR Scoare\n=========="} {KOR+=$2; print $1,$2} END{print "==========\nKOR Total: "KOR"\n=========="}' test.txt
        KOR Scoare
        ==========
        kim    90
        lee    100
        park    50
        ==========
        KOR Total: 340
        ==========

awk script

  • 실행할 내용 중 ' '안에 들어갈 내용을 스크립트 형태로 작성

  • 작성요령은 sed 스크립트와 동일

  • BEGIN, ACTION, END를 각 줄로 구분하여 처리

  • 세미콜론 부분은 줄바꿈으로 치환가능

      BEGIN{  FS="[:,]"
              OFS="\t"
              KOR=100
              print "KOR Score\n=========="}
    
      {KOR+=$2; print NR,$1,$2}
    
      END{print"==========\nKOR Total: "KOR"\n=========="}

실습예제 2

1. 다음과 같은 형식으로 출력하시오 (스크립트로 작성)
                                                                            Donor List
--------------------------------------------------------------------------------------------
Name                                 Phone                     Jan     Feb     Mar     AVG
--------------------------------------------------------------------------------------------
Mike Harrington         (510) 548-1278     250     100     175     175.00
Christian Dobbins     (408) 538-2358     155     90         201     148.67
Susan Dalsass             (206) 654-6279     250     60         50         120.00
....
--------------------------------------------------------------------------------------------
                                                                                Summary
--------------------------------------------------------------------------------------------
Total donate : $6137
Avg : $511
--------------------------------------------------------------------------------------------


BEGIN{ TOTAL=0
        OFS="\t"
        COUNT=0
        FS=":"
        print "                            donor list"
        print "================================================================"
        print "Name\t\tPhone\t\tJan\tFeb\tMar\tAVG"
        print "================================================================"
}
{ TOTAL=TOTAL+$3+$4+$5
  print $1"\t"$2,$3,$4,$5,($3+$4+$5)/3
  COUNT+=1
}
END{
        print "================================================================"
        print "Summary"
        print "================================================================"
        print "Total donate: $"TOTAL
        print "Avg: $"TOTAL/COUNT
        print "================================================================"
}
// 원하는 대로 출력은 되나 칸이 이름의 길이에 따라 칸이 어긋남

BEGIN{ TOTAL=0
        OFS="\t"
        COUNT=0
        FS=":"
        print "                            donor list"
        print "========================================================================"
        printf "%-20sPhone\t\tJan\tFeb\tMar\tAVG\n","Name"
        print "========================================================================"
}
{ TOTAL=TOTAL+$3+$4+$5
  printf "%-20s%s\t%3d\t%3d\t%3d\t%.2f\n",$1,$2,$3,$4,$5,($3+$4+$5)/3
  COUNT+=1
}
END{
        print "========================================================================"
        print "Summary"
        print "========================================================================"
        print "Total donate: $"TOTAL
        print "Avg: $"TOTAL/NR
        print "========================================================================"
}
//printf    를 이용한 출력 - 포맷을 정해서 출력하기 때문에 칸을 더 잘 맞출 수 있음