[Linux] AWK와 SED

728x90
텍스트 처리를 위한 유틸리티는 대표적으로 sed(stream editor)와 awk가 있음

1. AWK

  • awk
    데이터를 조작하고 리포트를 생성하기 위해 사용하는
    언어 리눅스에서 사용하는 awk는 GNU 버전의 gawk로 심볼릭 링크되어 있음
  • 간단한 연산자를 명령라인에서 사용할 수 있으며, 큰 프로그램을 위해 사용 가능
    awk는 데이터를 조작할 수 있기 때문에 쉘 스크립트에서 사용되는 필수 툴이며,
    작은 데이터베이스를 관리하기 위해서도 필수
  • Alfred Aho, Peter Weinberger, Brian Kernighan 3명의 제작자 이름 이니셜을 가져와서 awk

1.1 awk 프로그래밍 형식

  • awk 를 입력하고, 작은따옴표로 둘러싸인 패턴이나 액션을 입력,
    마지막에 입력 파일 이름 파일 이름을 지정하지 않으면 키보드 입력에 의한 표준 입력을 받음
  • awk는 입력된 라인들의 데이터들을 공백 또는 탭을 기준으로 분리,  
    $1부터 시작하는 각각의 필드 변수로 분리하여 인식

! AWK 형식

  • awk 'pattern' filename
  • awk '{action}' filename
  • awk 'pattern {action}' filename
  • 예시
$ vi awkfile 
홍 길동 3324 5/11/96 50354 
임 꺽정 5246 15/9/66 287650 
이 성계 7654 6/20/58 60000 
정 약용 8683 9/40/48 365000 

$ awk '{print $0}' awkfile 
> 
홍 길동 3324 5/11/96 50354 
임 꺽정 5246 15/9/66 287650 
이 성계 7654 6/20/58 60000 
정 약용 8683 9/40/48 365000 

$ awk '{print $1}' awkfile
> 홍 임 이 정 

$ awk '/길동/' awkfile 
> 홍 길동 3324 5/11/96 50354 

$ awk '/정/{print "\\t안녕하세요? " $1, $2 "님!"}' awkfile 
> 안녕하세요? 임 꺽정님! 안녕하세요? 정 약용님! 

$ awk '/홍/{print $1, $3}' awkfile 
> 홍 3324 $ df | awk '$4 < 100000' : |을 이용해 파이프라인 생성 
> devfs 368 368 0 100% 638 0 100% /dev
   map -hosts 0 0 0 100% 0 0 100% /net 
   map auto_home 0 0 0 100% 0 0 100% /home

! AWK 동작 원리

  1. awk는 파일 또는 파이프를 통해 입력 라인을 얻어와
    $0라는 내부 변수에 라인을 입력 각 라인은 레코드라고 부르고, newline에 의해 구분
  2. 라인은 공백을 기준으로 각각의 필드나 단어로 나뉨 필드는 $1부터 시작. 많게는 100개 이상의 필드 저장 가능
  3. 내장 변수인 FS라고 부르는 필드 분리자가 공백을 할당받음
    필드가 콜론/대시와 같은 문자에 의해 분리되면 새로운 필드 분리자로 FS의 값 변경 가능
  4. awk는 화면에 필드를 출력할 때 print 함수를 사용
  5. 콤마는 출력필즈 분리자(OFS)와 매핑되어 있으며 공백을 할당받음

! OFMT 변수

  • 숫자를 출력할 때 숫자 포맷 제어할 경우 사용. 간단히 printf를 사용할 수도 있지만, OFMT를 지정할 수 있음.
  • default는 %.6g로 소수점 6자리
$ awk 'BEGIN{OFMT="%.2f"; print 1.23412, 15E-3}' > 1.23 0.01

! printf 함수

  • 포매팅된 깔끔한 출력을 할 경우 사용
  • newline을 제공하지 않기 때문에 newline이 요구되면 \\n을 사용해야함
  • c : 문자, d : 10진수, f : 실수, x : 16진수
  • ``이 붙으면 좌측에서 시작되고 기본형이면 우측에서 시작
$ awk '{printf "The name is %-20s Number is %4d\\n", $1" "$2, $3}' awkfile
> The name is 홍 길동 Number is 3324
The name is 임 꺽정 Number is 5246
The name is 이 성계 Number is 7654
The name is 정 약용 Number is 8683

! awk -f 옵션

  • awk 액션과 명령이 파일에 작성되어 있다면 f 옵션을 사용
awk -f [awk 명령파일] [awk 명령을 적용할 텍스트 파일]
$ vi awkcommand {print "안녕하세요 " $1, $2"님"} {print $1, $2, $3, $4, $5}

$ awk -f awkcommand awkfile
> 안녕하세요 홍 길동님 홍 길동 3324 5/11/96 50354
안녕하세요 임 꺽정님 임 꺽정 5246 15/9/66 287650
안녕하세요 이 성계님 이 성계 7654 6/20/58 60000
안녕하세요 정 약용님 정 약용 8683 9/40/48 365000

! 레코드와 필드

  • 레코드- awk는 입력 데이터를 볼 수 없지만 포맷 또는 구조는 볼 수 있음. 각 라인(레코드)은 newline으로 분리
    - NR 변수 : 각 레코드들의 번호는 awk의 빌트인 변수 NR에 저장/ 레코드가 저장된 다음 NR의 값은 하나씩 증가
$ awk '{print NR, $0}' awkfile
> 1 홍 길동 3324 5/11/96 50354
2 임 꺽정 5246 15/9/66 287650
3 이 성계 7654 6/20/58 60000
4 정 약용 8683 9/40/48 365000

 

  • 필드
    - 빌트인 변수 FS는 입력 필드 분리자의 값을 가지고 있음
    - Default는 공백과 탭/ FS값 변경을 위해서는 F를 사용, F 다음 오는 문자가 새로운 필드 분리자
$ vi awkfile_FS 홍 길동 :3324 :5/11/96 :50354 임 꺽정 :5246 :15/9/66 :283502
$ awk -F: '/홍/{print $1, $2}' awkfile_FS > 홍 길동 3324
$ awk -F'[ :\t]' '/홍/{print $1, $2}' awkfile_FS > 홍 길동


! awk와 정규표현식

  • 정규표현식은 슬래시로 둘러싸인 문자들로 구성된 패턴
$ awk '/^정/{print $1, $2, $3}' awkfile > 정 약용 8683
$ awk '/^[A-Z][a-z]+ /' awkfile2 > Hong KilgDong 3324 5/11/96 50354
  • match 연산자(~) : 표현식과 매칭되는 것이 있는지 검사하는 연산자
$ awk '$2 !~ /g$/' awkfile2 : 2번 필드가 g로 끝나지 않는 라인 출력 > Lee Seongkye 7654 6/20/58 60000

 

  • POSIX 문자 클래스
$ awk '/[[:lower:]]+g[[:space:]]+[[:digit:]]' awkfile2 > Hong KilgDong 3324 5/11/96 50354


! 비교 표현식

  • 어떤 상태가 참일때만 액션이 수행
$ awk '$3 > 7000{print $1, $2}' awkfile > 이 성계 정 약용

 

  • 조건 표현식
    • 표현식을 검사하기 위해 ?와 :를 사용. if/else가 하는 역할과 같은 결과를 의미
$ awk '{max={$1 > $2) ? $1 : $2; print max}' filename if $1 > $2: max = $1 else: max = $2
  • 산술 연산자
    • 계산을 통해 필터링 가능
  • 논리연산자
    - && : AND 연산
    - || : OR 연산
    - ! : NOT 연산
$ awk '$3 > $5 && $3 <= 100' filename

1.2 awk 변수

  • BEGIN 패턴
    - awk가 입력 파일의 라인들을 처리하기 이전에 실행되며 액션 블록 앞에 놓임
    - 입력 파일 없이 테스트할 수 있고, 빌트인 내장 변수(OFS, RS, FS)들의 값을 변경할 경우 사용
  • END 패턴
    어떤 입력 라인과도 매칭되지 않고, 입력 모든 라인이 처리된 후 실행됨
    - BEGIN만 사용할 경우, 아규먼트 파일명을 적지 않아도 되지만
      END 블록을 사용할 경우엔 반드시 아규먼트 파일을 적어야 함
$ awk '/Tom/{count++}END{print "Tom was found " count " times."}' awkfile5
  •  
  • awk 리다이렉션
    - awk결과를 리눅스 파일로 리다이렉션할 경우 쉘 리다이렉션 연산자를 사용
    - 파일명은 큰따옴표로 둘러쌰아 함
    - > 심볼이 사용될 때 파일이 오픈되고 잘려짐
$ awk -F: '$4 >= 60000 {print $1, $2 > "new_file"}' awkfile5
  • getline 함수
    - 표준 입력, 파이프, 현재 처리되고 있는 파일로부터 입력을 읽기 위해 사용
      입력의 다음 라인을 가져와 NF, NR, FNR 빌트인 변수를 설정
    - 레코드가 검색되면 1을 리턴하고, 파일의 끝이면 0을 리턴. 에러가 발생하면 -1을 리턴
$ awk 'BEGIN{ "date " | getline d; split(d, year) ; print year[6]}' > 2009
$ awk 'BEGIN{while (getline < "/etc/passwd" > 0)lc++; print lc}' filename
  • awk 파이프
    - awk 프로그램에서 파이프를 오픈하고 다른 파이프를 오픈하기 전에 기존 파이프는 닫기
    - 파이프 심볼의 오른쪽 명령은 큰따옴표(““)로 둘러싸야 한다
  •  
$ awk '{print $1, $2 | "sort-r"}' cars

      - END블록에 close를 사용해 파이프를 꼭 닫아줘야 함

$ awk '{print $1, $2 | "sort -r"} END{close("sort -r")}' cars

2. SED

  • 정규 표현식을 사용하고, 기본 입출력은 표준 입력과 출력을 사용
  • 파이프를 통해 한쪽의 출력을 다른 쪽으로 넘길 수 있음
  • 주어진 주소 범위에 대해 입력의 어떤 줄을 처리할 것인지 결정
  • 주소 범위에는 라인 번호 또는 패턴을 사용 가능
    ex) 5d라고 하면 다섯 번째 라인을 삭제 windows/d는 “windows”를 포함하는 모든 라인을 삭제

2.1 sed 동작 원리

  • sed 스트림 에디터는 한번에 하나의 파일/ 하나의 입력으로부터 한 라인만 처리, 모니터로 출력
  • vi 에디터에서 사용 가능하며, 저장된 라인은 패턴 공간이라고 부르는 임시 버퍼에서 처리
  • 임시 버퍼에 있는 라인의 처리가 한번 끝나면 임시 버퍼에 있는 라인은 모니터로 출력
  • 라인이 처리된 다음 임시 버퍼에서 라인은 제거, 다음 라인이 임시 버퍼로 읽혀지고 처리/출력
  • 예제
    • 입력의 8번째 줄을 삭제
    /^$/d
    • 빈 줄을 모두 삭제
    1,/%$/d
    • 첫 줄부터 처음 나타내는 빈 줄까지 삭제
    /Jones/p
    • “Jones”를 포함하는 줄만 출력 (-n 옵션 사용)
    s/GUI//g
    • “GUI”가 나오는 줄에서 GUI를 ‘‘로 변경(삭제)
  • 8d
728x90

댓글()