Shell - awk 명령어 기본

03 May 2016

awk

  • 데이터 처리를 위한 유닉스 프로그래밍 언어.
  • awk 이름의 의미는, 언어 개발자 세 명의 이름의 이니셜을 따서 만든 것..
  • awk가 제작된 이후 다양한 버전이 나왔는데 구버전은 awk, 새로운 버전은 nawk, GNU 버전은 gawk 라는 명령어로 사용한다.

awk 기본 사용법

  • 패턴command로 이루어진다.
  • 패턴 부분에는 정규식이나 조건 표현식(true/false)이 올 수 있다.
  • 정규식 예: /^hello$/
  • 조건 표현식 예: $1 >= 0
  • command 부분은 c언어와 마찬가지로 조건연산자, 반복연산자, 산술연산자, 논리연산자, 삼항연산자, 증감연산자, 대입연산자 등을 사용 가능하다. 문법 또한 c언어와 거의 유사하다.
  • 아무 command를 지정하지 않을 경우, 패턴에 매칭된 줄을 그대로 출력하는 기본 동작을 수행한다.
  • command 부분은 { }로 감싼다.
    • command 예: { if($1 >= 0){ print "hello"; } }

정규표현식

  • 정규표현식을 사용할 때는 슬래시 /로 감싼다.
  • grep, sed와 마찬가지로 정규표현식 메타문자를 사용할 수 있다. 단 단어 지시자 \<\>, 캡처 \(\), 반복 지시자 \{\}는 사용할 수 없다.
$ awk '/^Hello/' file	# 정규식 /^Hello/에 매칭되는 줄을 그대로 출력한다.

Field Separator

awk는 필드 구분자를 기준으로 각 줄의 필드를 나누어 처리한다. 필드 구분자는 FS라는 이름의 내장변수로 관리되며 기본값은 탭/스페이스이다.

$ cat file
Hello 1
Hi 2
Haha 3

$ awk '{print $1}' file
hello
hi
haha

디폴트 FS인 스페이스에 의해 필드가 나뉘었다. print 함수를 이용하여 첫 번째 필드 $1를 출력한다. command 부분은 { }로 묶는다.

$ cat file
hello 1
hi 2
haha 3

$ awk '/hello/{print $1,$2}' file
hello 1

정규식 패턴 /hello/에 매칭된 모든 줄의 첫 번째 필드($1)와 두 번째 필드($2)를 출력한다. 이 때 $1,$2 사이에 콤마,를 사용했는데, 출력된 결과에는 콤마 대신 공백이 들어갔다. print 명령에서의 콤마는 출력 필드 구분자를 의미하는 OFS라는 이름의 내장변수의 값으로 치환되기 때문이다. OFS의 기본값은 스페이스라서 콤마 대신 스페이스가 출력되었다.

$ cat file
hello 1
hi 2
haha 3

$ awk '$2 > 1' file
hi 2
haha 3

조건 표현식 $2 > 1이 참인 줄만 출력한다. 즉 두 번째 필드($2)의 값이 1 보다 큰 줄만 출력한다.

$ cat file
hello 1
hi 2
haha 3

$ awk '{ print $0 }' file
hello 1
hi 2
haha 3

$0은 필드를 나누지 않은 상태의 전체 레코드를 의미하는 내장변수이다. 즉 줄 내용 전체가 그대로 출력된다.

$ awk -F: {print $1} file    # 콜론(:) 을 필드 구분자로 사용

$ awk -F'[:,]' {print $1} file    # 콜론(:)과 콤마(,)를 필드 구분자로 사용

-F 옵션을 사용하여 필드 구분자를 직접 지정할 수도 있다.

printf

  • 출력 포맷을 지정하고 싶을 때는 print 대신 printf 함수를 사용한다. 사용법은 C언어의 printf와 유사하다.
  • printf는 print와 달리 개행(new line)처리가 되지 않으므로, 필요시 \n을 직접 명시한다.
문자 설명
%c character
%s string
%d, %ld 10진수, long 10진수
%u, %lu unsigned 10진수
%x, %lx 16진수
%o, %lo 8진수
%e 부동소수점, e-표기법
%f 부동소수점
%g 부동소수점. %e와 %f 중 적은 자릿수를 차지하는 표기법을 사용.
$ cat file
hello 1
hi 2
haha 3

$ awk '{ printf "word is %-10s and number is %8d and %03d\n", $1, $2, $2}' file
word is hello      and number is        1 and 001
word is hi         and number is        2 and 002
word is haha       and number is        3 and 003
  • %-10s : 10자리의 공간에 string 출력. 대시-왼쪽 정렬을 의미.
  • %8d : 8자리의 공간에 10진수 출력. 기본으로 오른쪽 정렬.
  • %03d : 3자리의 공간에 10진수 출력. 남는 공간은 0으로 채움.

내장변수

위에서 언급한 FS, OFS 외에도 여러 내장변수가 존재한다.

  • 내장변수의 이름은 대문자로 이루어져 있다.
  • 내장변수는 패턴 및 command 내에서 사용할 수 있으며 값을 재설정 할 수도 있다.
이름 설명
ARGC 커맨드라인 매개변수의 갯수
ARGV 커맨드라인 매개변수의 배열
FILENAME 입력 파일의 이름
FNR 현재 파일의 레코드 갯수
NF 현재 레코드의 필드 갯수
NR 레코드 번호
OFMT 숫자를 위한 출력 포맷
OFS 출력 필드 구분자
ORS 출력 레코드 구분자
RS 입력 레코드 구분자
RLENGTH match 연산자로 매칭된 부분의 길이
RSTART match 연산자로 매칭된 부분의 시작 위치
SUBSEP 배열 구분자

NR, NF

$ cat file
hello 1
hi 2
haha 3

$ awk '/haha/{ print NR, $1, NF }' file
3 haha 2

NR은 파일 내에서 해당 레코드의 번호를 나타내는 내장변수이다. NF은 해당 줄의 필드 갯수를 나타내는 내장변수이다.

OFMT

$ awk `{ OFMT="%.2f"; print 1.234567, 12E-2; }` file

OFMT는 print 함수의 출력 포맷을 지정하는 내장변수이다.

ARGV, ARGC

  • ARGV는 shell 커맨드라인에서 입력받은 매개변수가 순서대로 저장되는 내장 배열이다. nawk 에서만 지원한다.
  • nawk라는 명령어 자체도 매개변수에 포함되며(ARGV[0]), 명령어의 옵션들은 포함되지 않는다.
  • ARGC는 커맨드라인 매개변수의 갯수가 저장되는 내장 변수이다.
$ nawk -F',' '{ for(i=0; i<ARGC; i++){ print ARGV[i]; } }' filename1 filename2
nawk        # ARGV[0]
filename1   # ARGV[1]
filename2   # ARGV[2]

match 연산자 (~, tilde)

특정 필드의 값이 정규표현식에 매칭되는지 검사할 때는 ~를 사용한다. 매칭되지 않는지 검사할 때는 !~를 사용한다.

$ awk '$1 ~ /[Hh]ello/' file    # 첫 번째 필드가 Hello, hello인 레코드를 출력
$ awk '$1 !~ /^hello/{ print $1 }' file

variable

  • 정수, 부동소수점 실수, 문자열 등을 위한 변수를 사용할 수 있다.
  • awk의 변수는 선언 없이 바로 사용할 수 있으며, 사용되는 형태에 따라 0이나 ““으로 초기화된다.
  • awk 내에서의 변수는 shell에서의 변수와 기본적으로 호환되지 않는다. shell의 변수를 awk로 전달하기 위해서는 -v 옵션을 사용할 수 있다.

-v 옵션

  • -v 옵션을 통해 전달된 변수는 awk의 BEGIN 문에서 사용할 수 있다,

BEGIN

  • awk가 입력 파일을 처리하기 전에, 수행해야 될 전처리 동작들을 지정할 수 있다.
  • OFS, RS, FS 등 내장변수의 값을 변경하거나, 사용자 정의 변수의 초기화 등을 하기 위해 쓰인다.
$ cat file
hello:1
hi:2
haha:3

$ awk 'BEGIN{ FS=":"; OFS="\t"; } {print $1,$2 }' file
hello	1
hi	2
haha	3
$ awk 'BEGIN{ print "Hello, world!" }
  • BEGIN 블럭은 awk가 입력 파일을 열기 전에 수행되므로, 입력 파일 없이 BEGIN 블럭만 실행할 수도 있다.

END

  • 입력 파일의 모든 줄의 처리가 끝난 후에, 맨 마지막으로 실행할 동작들을 지정할 수 있다.
$ awk '{print $1} END{ print "process done."; }' file
$ awk 'BEGIN{ sum = 0; } /hello/{ sum += $1; } END{ print "sum is "sum; }' file

이렇게 BEGIN, 패턴, command, END 를 모두 함께 사용할 수 있다.

Array in awk

  • awk의 배열은 연관 배열(associative array)로, key와 value를 갖는 Hash Set 이다.
  • 변수와 마찬가지로, 미리 선언하지 않아도 바로 사용할 수 있다. (사용과 동시에 생성된다.)
  • 배열이 사용되는 타입에 따라 0이나 빈 문자열로 적당히 초기화된다.

배열의 순회

for (item in array) {
	print array[item];
}

delete

내장 함수 delete는 배열 요소를 삭제하는데 사용한다. - delete(array[item]);

$ awk '{ arr[x++] = $2; } END{ for(i in arr){ delete(arr[i]); } }' filename

기타 내장함수

sub

정규표현식에 매칭되는 부분을 지정한 문자열로 치환한다. longest 매칭이며, 레코드 내에서 여러번 매칭되어도 맨 왼쪽의 일치하는 부분에 대해서 한번만 치환한다. 치환할 대상을 지정하지 않으면 현재 레코드 전체($0)에 대해서 치환한다.

  • sub(/정규표현식/, “치환할 문자열”, “치환할 대상”);
  • sub(/정규표현식/, “치환할 문자열”); - $0 에 대해서 치환

gsub

sub와 유사하나 레코드 내에서 정규표현식이 매칭될 때마다 치환한다.

index

문자열 내에서 찾는 문자열이 나타나는 위치를 리턴한다. 위치는 1부터 시작하여 계산된다.

  • index(“문자열”, “찾는 문자열”);

length

문자열의 길이(문자 수)를 구한다.

  • length(“문자열”)
  • length 문자열을 지정하지 않으면 현재 레코드 전체($0)에 대한 길이를 구한다.

substr

문자열 내에서 지정한 위치부터의 부분 문자열을 반환한다.

  • substr(문자열, 시작 위치);
  • substr(문자열, 시작 위치, 문자열 길이)

match

문자열 내에서 주어진 정규표현식에 매칭되는 패턴이 있으면 시작 위치를 반환하고, 없으면 0을 리턴한다. 추가적으로 문자열이 매칭될 경우, 패턴의 시작 위치를 내장변수 RSTART에 저장하고 부분 문자열의 길이는 RLENGTH에 저장한다. 이는 match 함수로 매칭 여부를 확인 후 substr을 사용하는데 유용하다. 단 nawk에서만 동작한다.

  • match(“문자열”, /정규표현식/);

split

문자열을 단어 단위로 나누어 배열에 저장한다. 필드 구분자를 지정하지 않으면, 내장변수 FS의 값이 디폴트로 사용된다.

  • split(문자열, 저장할 배열, 필드 구분자);
  • split(문자열, 저장할 배열); - 내장변수 FS의 값을 필드 구분자로 사용
$ awk 'BEGIN{ split("05/01/2016", date, "/"); print date[3]; }'
2016

int

소숫점 이하를 버리고 정수부만 반환한다.

  • int(x)

rand

0~1 사이의 난수를 반환한다.

  • rand()
GMT와 UTC 차이, summer time Mocks Aren't Stubs