Functions
awk 는 기본적으로 데이터 처리를 위해 사용할 수 있는 string 함수, numeric 함수 같은 여러 가지 유용한 built-in 함수들을 제공합니다. 이 함수들에 대해서는 뒷부분에서 따로 알아보기로 하고 여기서는 사용자가 정의하여 사용할 수 있는 함수에 대해서 알아보겠습니다.
awk 에서 함수에 인수가 전달되는 방식은 다음과 같습니다.
scalar 변수 : passing by value
array 변수 : passing by reference
$ awk 'BEGIN { v = 100; f(v); print v } function f(p) { p = 200 }'
100
$ awk 'BEGIN { v[1] = 100; f(v); print v[1] } function f(p) { p[1] = 200 }'
200
사용자 함수 정의
함수를 정의할 때는 function
or func
키워드를 사용할 수 있고 함수 정의는 블록 밖에서 해야 됩니다.
BEGIN, END 블록이나 { }
내에서 하면 오류가 발생합니다.
awk 는 실행하기 전에 먼저 전체 코드를 읽어들여 해석하므로 함수의 위치는 순서에 관계없이 작성할 수 있습니다.
구 버전에서는 함수명과 이어지는 괄호 사이에 공백이 있으면 오류가 발생할 수 있습니다.
#!/usr/bin/awk -f
BEGIN {
FS = "\t";
}
{
log_level = $2;
}
(log_level == "ERROR") {
print red($0)
}
(log_level == "WARN") {
print yellow($0)
}
END {
}
function red(str) {
return "\033[1;31m" str "\033[0m"
}
function yellow(s) {
return "\033[1;33m" str "\033[0m"
}
local 변수를 설정하는 방법
awk 는 local 변수 설정을 함수의 매개변수를 설정하는 ( )
에서 같이 합니다.
따라서 매개변수와 로컬변수를 구분하기 위해
관례적으로 다음과 같이 공백을 두어 작성합니다.
왼쪽에 매개변수가 위치하고 오른쪽에 로컬변수가 위치합니다.
매개변수만 사용될 경우는 첫 번째 처럼 따로 공백이 필요 없지만
로컬변수만 사용될 경우는 두 번째와 같이 앞부분에 공백을 두어 로컬변수 임을 나타내면 되겠습니다.
return
함수 실행을 종료해야 되거나 값을 전달해야 될 경우 return 문을 사용할 수 있습니다.
return [expression]
Indirection
@
문자를 이용하면 함수명을 인수로 전달해서 실행시킬 수도 있습니다.
$ awk 'BEGIN {
call("foo", 100)
call("bar", 200)
}
function call(fun, num) { @fun(num) }
function foo(arg) { print "Im foo :", arg }
function bar(arg) { print "Im bar :", arg }
'
Im foo : 100
Im bar : 200
Recursion 사용시 주의할 점
awk 는 함수 재귀 호출시 매 호출마다 새로 레코드를 읽어들여 사용한다면
기본적으로 현재 함수에서 사용 중인 $0
,$1
,$2
,...NF
,NR
값들이 유지가 안됩니다.
따라서 return 후에도 정상적으로 기존 값을 사용하려면 먼저 함수 호출전에
local 변수에 저장해 놓는 것이 필요합니다.
그리고 return 후에는 저장한 local 변수값을 다시 $0
에 대입해 사용하면 됩니다.
$ cat sample.txt
111 222 333
444 555 666 AAA BBB
777 888 999
# NR 값도, NF 값도, 각 필드 value 도 정상적으로 표시되지 않는다.
$ awk 'BEGIN{ readf() }
function readf( i ) {
while ( getline > 0 ) {
while ( ++i <= NF ) {
print "record " NR " : field " i " : " $i
readf()
}
}
}' sample.txt
record 1 : field 1 : 111 # AAA, BBB 값은 표시도 안된다.
record 2 : field 1 : 444
record 3 : field 1 : 777
record 3 : field 2 : 888
record 3 : field 3 : 999
record 3 : field 2 : 888
record 3 : field 3 : 999
record 3 : field 2 : 888
record 3 : field 3 : 999
------------------------------------------------------
# 먼저 $0, NF, NR 값을 local 변수에 저장후 함수를 호출
$ awk 'BEGIN{ readf() }
function readf( i, z, nr ) {
while ( getline > 0 ) {
z = $0; nr = NR
while ( ++i <= NF ) {
print "record " NR " : field " i " : " $i
readf()
# return 후에는 $0 = z 를 해줘야 함수 호출전 상태로 $1,$2,...NF 값이 설정된다.
$0 = z; NR = nr
}
}
}' sample.txt
record 1 : field 1 : 111
record 2 : field 1 : 444
record 3 : field 1 : 777
record 3 : field 2 : 888
record 3 : field 3 : 999
record 2 : field 2 : 555
record 2 : field 3 : 666
record 2 : field 4 : AAA
record 2 : field 5 : BBB
record 1 : field 2 : 222
record 1 : field 3 : 333
awk 는 C/C++ 와 달리 recursion 횟수에 제한이 없습니다.
다음 명령을 실행해보면 메모리 사용량이 계속해서 증가되는 것을 볼 수 있습니다.
$ awk 'BEGIN { i=0; f(1,2,3,4); }
function f(a,b,c,d) { print "recursion : " i++; f(1,2,3,4) }'
. . .
. . .
recursion : 543784
recursion : 543785
recursion : 543786
recursion ^C # Ctrl-c 종료
다음은 recursion 을 이용해 자손 프로세스를 출력하는 예입니다.
# $1 는 PID, $4 는 PPID
$ awk -v PPID=`pidof gdm3` -v FPAT='[^ ]+|\\([^\\)]*\\)' '
{ a[$1] = $4 } END{ print PPID; desc(PPID) }
function desc( ppid, i) {
for ( i in a ) { if ( a[i] == ppid ) { print i; desc(i) }}
}
' /proc/[0-9]*/stat | xargs ps f
PID TTY STAT TIME COMMAND
1510 ? Ssl 0:00 /usr/sbin/gdm3
1679 ? Sl 0:00 \_ gdm-session-worker [pam/gdm-autologin]
1882 tty2 Ssl+ 0:00 \_ /usr/lib/gdm3/gdm-x-session --register-session ...
1890 tty2 Sl+ 5:26 \_ /usr/lib/xorg/Xorg vt2 -displayfd 3 -auth ...
2380 tty2 S+ 0:00 \_ /bin/sh /usr/bin/startkde
2586 ? Ss 0:00 \_ /usr/bin/ssh-agent /usr/bin/im-launch ...
3106 tty2 S+ 0:00 \_ kwrapper5 /usr/bin/ksmserver