Variables

awk 는 변수를 사용할 때 일반 프로그래밍 언어와 달리 먼저 선언을 하지 않고 사용해도 됩니다. 그러면 자동으로 전역변수로 설정이 되고 프로그램이 종료될 때까지 유지가 됩니다. awk 는 변수에 타입을 두지 않기 때문에 자유롭게 숫자나 스트링을 대입해 사용할 수 있습니다. 이와같은 특징은 간단히 쉘 스크립트를 작성할 때 편리합니다.

# 동일한 변수에 숫자나 스트링을 자유롭게 대입할 수 있다.
myvar = 45; myvar = "foo bar"

awk 에서 사용하는 키워드나 함수명을 변수명으로 사용할 수 없습니다.

$ awk 'BEGIN { in = 123 }'                # in 키워드
awk: cmd. line:1: BEGIN { in = 123 }
awk: cmd. line:1:         ^ syntax error

# 다음은 모두 오류입니다.
$ awk 'BEGIN { index = 123 }'             # index() 함수
$ awk 'BEGIN { length = 123 }'            # length() 함수
$ awk 'BEGIN { func = 123 }'              # func 키워드 (function 단축형)

존재하지 않는 변수는 산술연산에서는 0 과 같고 스트링 연산에서는 "" 과 같습니다.

# 현재 bb 는 존재하지 않는 변수 
$ awk 'BEGIN { aa=0; if ( aa == bb ) print "yes" }' 
yes

$ awk 'BEGIN { aa=""; if ( aa == bb ) print "yes" }' 
yes

하지만 scalar 변수와 array 변수는 구분합니다.

awk 에서는 변수명이 처음 사용될 때 scalar 변수인지 array 변수인지가 결정됩니다. 그러므로 이후에 array 변수를 scalar 변수로 사용한다 든지 scalar 변수를 array 변수로 사용하게 되면 오류가 발생합니다. 심지어 array 변수를 delete 한 후에도 scalar 변수로 사용할 수 없습니다.

# 변수 AA 가 처음 사용될 때 array 변수로 결정됨. 이후에 scalar 변수로 사용하여 오류 발생
$ awk 'BEGIN{ AA[1]="foo"; AA="bar" }' 
awk: cmd. line:1: fatal: attempt to use array 'AA' in a scalar context

# 변수 AA 가 처음 사용될 때 scalar 변수로 결정됨. 이후에 array 변수로 사용하여 오류 발생
$ awk 'BEGIN{ AA="foo"; AA[1]="bar" }' 
awk: cmd. line:1: fatal: attempt to use scalar 'AA' as an array

print 문도 scalar context 이므로 array 변수를 사용할 수 없습니다.

$ awk 'BEGIN { AA[1]="foo"; print AA }' 
awk: cmd. line:1: fatal: attempt to use array `AA' in a scalar context

$ awk 'BEGIN { AA[1]="foo"; print AA[1] }' 
foo

$ Variables

awk 에서 사용되는 특수한 형태의 변수라고 할 수 있습니다. 레코드 값을 나타내는 $0, 각각의 필드 값을 나타내는 $1, $2, $3, ... 로 구성됩니다. 여기서 숫자 부분은 변수로 대체해 사용할 수 있습니다.

$ echo 11 22 33 | awk '{ print $1, $2, $3 }'
11 22 33

# 숫자 부분을 변수로 대체해 사용
$ echo 11 22 33 | awk '{ a=1; b=2; c=3; print $a, $b, $c }'
11 22 33

$ echo 11 22 33 | awk '{ v=1; print $(v++), $(v++), $v }'
11 22 33

# NF 는 레코드의 필드 개수를 나타내는 builtin 변수
$ echo 11 22 33 | awk '{ print $(NF-2), $(NF-1), $NF }'
11 22 33
-----------------------------------------------------------

# 50점 이하 과목이 하나라도 있는 사람 출력하기
$ cat file
이름    국어    산수    사회    자연    미술
AAA     88      76      97      78      35
BBB     77      28      56      43      55
CCC     90      65      53      80      65

$ awk 'NR!=1 { 
    for (i=0; NF-i > 1; i++) 
        if ($(NF-i) < 50) { print; next } 
}' file
AAA     88      76      97      78      35
BBB     77      28      56      43      55

awk 명령에 변수값 전달 방법

awk 는 코드를 작성할 때 single quotes 을 사용하므로 본문에 shell 변수를 사용하지 못하는데요. 명령의 -v 옵션이나 인수 전달 방식을 통해서 shell 변수값을 전달할 수 있습니다.

첫 번째 방법은 데이터 파일을 awk 에 인수로 전달할 때처럼 변수값을 전달하는 방법입니다.
이렇게 전달된 변수값은 순서에 따라서 이후의 데이터 파일에 적용됩니다.

$ cat file1
file1 record 1
file1 record 2
file1 record 3

$ cat file2
file2 record 1
file2 record 2
file2 record 3

# 순서에 따라서 AA=111 값은 file1 레코드에 적용되고
# AA=222 값은 file2 에 적용되는 것을 볼 수 있습니다.
$ awk '{ print AA, $0 }' AA=111 file1 AA=222 file2
111 file1 record 1
111 file1 record 2
111 file1 record 3
222 file2 record 1
222 file2 record 2
222 file2 record 3

따라서 다음과 같이 설정하면 데이터 파일 별로 RS, FS 를 설정할 수 있습니다.

# file1 에는 RS='#' FS=',' 값이 적용되고
# file2 에는 RS='@' FS=':' 값이 적용된다.
$ awk '{ . . . }' RS='#' FS=',' file1 RS='@' FS=':' file2

BEGIN 블록은 레코드를 읽어들여 명령 사이클을 시작하기 전에 실행되므로 위와 같이 설정한 변수는 BEGIN 블록에는 적용되지 않는데요. 소스코드 전체에 적용시키려면 awk 명령의 -v 옵션을 사용해야 합니다.

$ awk 'BEGIN{ print AA, BB }' AA=11 BB=22
$

$ awk -v AA=11 -v BB=22 'BEGIN{ print AA, BB }'
11 22

마지막으로 FS 변수는 자주 사용되므로 따로 -F 옵션을 두고 있습니다.

$ awk -F, 'BEGIN{ print "FS is: " FS }'
FS is: ,

# FS 값은 ',' or ':'
$ awk -F ',|:' 'BEGIN{ print "FS is: " FS }'
FS is: ,|:

awk 에 shell 변수값을 전달할 때 주의할 점

awk 에 변수값을 전달할 때 사용하는 = 문자는 shell 에서 사용하는 대입 연산자가 아닙니다. 그러므로 전달하고자 하는 변수값이 공백으로 분리되어 있으면 double quotes 을 해야 오류가 생기지 않습니다.

$ MYVAR="foo bar"    # shell 변수

# -v AA=$MYVAR 는 -v AA=foo bar 와 같게 되므로 오류가 발생합니다.
$ awk -v AA=$MYVAR 'BEGIN { ... }'      # error !

# double quotes 을 해야 합니다.
$ awk -v AA="$MYVAR" 'BEGIN { ... }'    # OK
$ awk -v "AA=$MYVAR" 'BEGIN { ... }'    # OK

SYMTAB 변수를 이용한 indirection

awk 는 프로그램을 실행하기 전에 먼저 전체 코드를 읽어들여 파싱 하는데요. 이때 빌드 된 심볼 테이블을 SYMTAB built-in 변수를 통해 접근할 수가 있습니다. 이 변수를 이용하면 일종의 indirection 을 할 수 있습니다.

$ awk '
BEGIN {
    foo = 100
    SYMTAB["foo"] = 200
    print foo

    bar[2] = 300
    SYMTAB["bar"][2] = 400
    print bar[2]
}'
200
400

$ awk '
BEGIN {
    answer = 10.5
    multiply("answer", 4)
    print "The answer is", answer
}
func multiply(var, amount) {
    SYMTAB[var] *= amount
}'
The answer is 42