Tips

shell 변수값 설정 방법

awk 는 child process 로 실행되므로 실행 중에 현재 shell 의 변수값을 설정할 수 없습니다. 하지만 shell eval 명령을 활용하면 shell 변수값을 설정할 수 있습니다.

for tuple in \
    INTEL_FAM6_KABYLAKE_DESKTOP,0x0B,0x80  \
    INTEL_FAM6_SKYLAKE_X,0x03,0x0100013e   \
    INTEL_FAM6_BROADWELL_CORE,0x04,0x28    \
    INTEL_FAM6_HASWELL_ULT,0x01,0x21       \
    INTEL_FAM6_IVYBRIDGE_X,0x04,0x42a      \
    INTEL_FAM6_SANDYBRIDGE_X,0x07,0x712
do  # 명령치환을 이용한 model, stepping, ucode 변수값 설정
    model=$(echo $tuple | cut -d, -f1)   
    stepping=$(( $(echo $tuple | cut -d, -f2) ))
    ucode=$(echo $tuple | cut -d, -f3)

    if [ "$cpu_model" = "$model" ] && [ "$cpu_stepping" = "$stepping" ]
    then
        . . . .
        . . . .
done
-------------------------------------------------------------------------

for tuple in \
    . . .
do  # eval 과 awk 를 활용하여 간단히 작성
    eval "$( echo "$tuple" | 
    awk -F, '{ printf "model=%s; stepping=%s; ucode=%s;",$1,strtonum($2),$3 }' )"

    if [ "$cpu_model" = "$model" ] && [ "$cpu_stepping" = "$stepping" ]
    then
        . . . .
        . . . .
done

shell 의 declare, local, export, eval 를 이용하여도 변수값을 설정할 수 있습니다.

$ f1() {
    local $( awk 'BEGIN{ print "AA=100 BB=*" }' ) 
    echo "$AA : $BB"
}

$ f1
100 : *

shell 의 read 명령과 here document 를 이용한 방법
명령치환이 일어나야 하므로 END 구분자를 escape 하면 안됩니다.

$ IFS=, read AA BB CC <<END
$( awk 'BEGIN{ print "100,,*" }' )     # 두 번째는 empty 값
END

$ echo "$AA : $BB : $CC"
100 :  : *

bash 의 경우 다음과 같은 방법도 가능합니다.

# here string 을 이용
$ IFS=, read AA BB CC <<< $( awk 'BEGIN{ print "100,,*" }' )
$ echo "$AA : $BB : $CC"
100 :  : *

# source 명령과 프로세스 치환을 이용
$ source <( awk 'BEGIN{ print "AA=100 BB=*" }' )
$ echo "$AA : $BB"
100 : *

here document 를 이용한 코드 작성방법

shell 에서 제공되는 <<WORD 형식을 이용하는 here document 기능은 문서를 작성할 때 single, double quotes 을 escape 할 필요 없이 자유롭게 사용할 수 있는 장점이 있습니다. 그런데 본문 내에서 기본적으로 $ 문자로 시작하는 shell 변수가 확장되기 때문에 awk 스크립트를 작성할 때는 다음과 같이 구분자로 사용되는 단어를 escape 하여 shell 변수 확장을 disable 해줘야 합니다.

# heredoc 은 stdin 에 연결되므로 '-f -'  옵션을 사용 ('-' 는 stdin 을 나타냄)
$ awk -f - <<\EOF     # <<\EOF
BEGIN { ... }
EOF

$ awk -f - <<'ABC'    # <<'ABC'
BEGIN { ... }
ABC

# 파이프 로인해 stdin 이 사용 중이므로 -f /dev/fd/3 를 사용
$ command ... | awk -f /dev/fd/3 3<<\EOF
BEGIN { ... }
EOF

awk 내에서 here document 작성법

awk 에서 shell 명령문을 작성할 때는 기본적으로 스트링을 이용하므로 heredoc 과같이 개행이 필요한 명령문에서는 직접 newline 문자를 사용해야 합니다.

$ awk 'BEGIN {            # 오류
    cmd = "cat <<END                     
        111
        222
        333
END"
system(cmd)
}' 
awk: cmd. line:2:     cmd="cat <<END
awk: cmd. line:2:         ^ unterminated string
awk: cmd. line:2:     cmd="cat <<END
awk: cmd. line:2:         ^ syntax error
---------------------------------------------------

$ awk 'BEGIN { 
cmd = "cat <<END\n111\n222\n333\nEND"       # 직접 newline 문자를 사용
system(cmd)
}' 
111
222
333

만약에 shell 에서처럼 heredoc 을 작성하려면 다음과 같이하면 됩니다.

$ awk 'BEGIN { 
cmd = "cat <<END\n\
      ${SHELL}\n\
      ${PWD}\n\
END"
system(cmd)
}' 
      /bin/bash     # export 된 값으로 실질적으로는 sh 에의해 실행됩니다.
      /home/mug896/tmp

$ awk 'BEGIN { 
cmd = "cat <<\\END\n\
      ${SHELL}\n\
      ${PWD}\n\
END"
system(cmd)
}' 
      ${SHELL}
      ${PWD}

printf 문에서 천 단위 콤마 넣기

awk 는 스크립트 내에서 필드 변수에 $ 문자를 사용하기 때문에 shell 변수로 해석되는 것을 방지하기 위해 single quotes 을 이용해 작성합니다. 그런데 printf 문에서 천 단위 콤마를 넣을 때 사용하는 %'d 포멧 스트링의 경우 ' 가 사용되어 문제가 되는데요. 이때는 \047 escape 문자를 사용하거나 스크립트 작성에 사용된 single quotes 을 분리한 후에 \'"'" 를 이용해 연결하면 됩니다.

echo $(echo 2^64 | bc) | LC_ALL=en_US.UTF-8 awk '{ printf "%'d\n", $1 }'        # Error
> ^C

$ echo $(echo 2^64 | bc) | LC_ALL=en_US.UTF-8 awk '{ printf "%\047d\n", $1 }'    # OK
18,446,744,073,709,551,616

$ echo $(echo 2^64 | bc) | LC_ALL=ko_KR.UTF-8 awk '{ printf "%'\''d\n", $1 }'    # OK
18,446,744,073,709,551,616

$ echo $(echo 2^64 | bc) | LC_ALL=ko_KR.UTF-8 awk '{ printf "%'"'"'d\n", $1 }'   # OK
18,446,744,073,709,551,616
.....................................................................

$ awk '
BEGIN {
    if ( "pidof chrome | awk \047{ print NF }\047" | getline count )
        print "total chrome pids : " count
}'
total chrome pids : 45

$ awk '
BEGIN {
    if ( "pidof chrome | awk '\''{ print NF }'\''" | getline count )
        print "total chrome pids : " count
}'
total chrome pids : 45

include 할 수 있다

@include 키워드 와 -i 명령 옵션을 이용해 스크립트 파일을 include 해 사용할 수 있습니다. 이 기능은 awk 스크립트 파일을 라이브러리 용도로 활용하기 위한 것으로 한번 include 되어 실행되면 이후에 중복 include 가 되어도 다시 실행되지 않습니다.

파일을 찾을 때는 AWKPATH 환경 변수를 이용하는데 다음과 같이 직접 경로를 지정할 수도 있습니다.

@include "../test1"
@include "/home/abc/awklib/test2"

$ cat test1.awk 
BEGIN {
    print "This is script test1."
}
@include "test2"
.................................

$ cat test2.awk 
BEGIN {
    print "This is script test2."
}
.................................

$ awk -i test1 'BEGIN{ print "this is main" }'
This is script test1.
This is script test2.
this is main

timeout 설정 방법

awk 는 blocking read 에 대해서 millisecond 단위로 timeout 을 설정할 수 있습니다. 이것은 PROCINFO 변수에 값을 설정하는 것으로 이루어지며 개별 operation 별로 설정할 수가 있습니다. input 값이 도착하지 않아 timeout 이 되면 blocking 이 해제되고 -1 이 반환 됩니다. 이때 ERRNO 변수값도 함께 설정됩니다.

PROCINFO[ "input_name", "READ_TIMEOUT" ] = timeout in milliseconds

# timeout 이 되면 getline 에서 -1 이 반환됩니다.
$ awk 'BEGIN {
    PROCINFO["/dev/stdin", "READ_TIMEOUT"] = 5000   # 5초
    printf "enter number : "
    while (getline var < "/dev/stdin" > 0) {
        print "entered: " var
        printf "enter number : "
    }
    print "ERRNO: " ERRNO
}'
enter number : 111
entered: 111
enter number : 222
entered: 222
enter number : ERRNO: Connection timed out