Escape sequences

awk 를 처음 시작할 때 많이 실수하는 부분이 FS, RS 변수를 설정할 때 사용하는 escape sequence 부분입니다. awk 에서는 기본적으로 스트링이 double quotes 안에서 처리됩니다. 따라서 escape 할때 사용되는 \ 문자가 awk 에 전달되려면 \\ 두개를 사용해야 합니다.

$ echo '111(222)333' | awk -F '\(|\)' '{print $2}'        # wrong
awk: warning: escape sequence `\(' treated as plain `('
awk: warning: escape sequence `\)' treated as plain `)'


$ echo '111(222)333' | awk -F '\\(|\\)' '{print $2}'      # good
222

아래 escape sequence 테이블에 있는 문자를 사용할 때는 \ 하나만 사용해도 되지만 그 외 regex 에서 사용되는 특수문자를 escape 한다던지 할때는 \\ 두개를 사용해야 합니다.

Escape Sequence Character represented
\\ \ 문자
\a alert (bell)
\b backspace
\f form feed
\n newline
\r carriage return
\t horizontal tab
\v vertical tab
\NNN 8 진수 ASCII 문자 NNN
\xHH 16 진수 ASCII 문자 HH
\/ regex 상수에서 사용되는 / 문자
\" double quotes 에서 사용되는 " 문자

예를 들어 newline 이나 tab 문자를 설정하는 경우는 다음과 같이 두 가지 형태가 모두 가능합니다. 첫 번째 경우는 double quotes 에서 escape sequence 가 처리되여 직접 newline, tab 문자가 전달되는 경우고 두 번째는 FS 값이 \t, \n 와 같이 escape sequence 형태로 전달됩니다.

$ awk -F '\t' '{ ... }'            # 첫 번째 OK

$ awk -F '\\t' '{ ... }'           # 두 번째 OK

$ awk 'BEGIN{ FS="\n" } ...'       # OK

$ awk 'BEGIN{ FS="\\n" } ...'      # OK

변수값 설정시 escape sequence 사용

RS, FS 같은 변수는 설정된 값이 문자가 두개 이상이면 regex 로 해석됩니다. 따라서 regex 에서 사용되는 문자를 일반 문자로 사용하려면 escape 해야 합니다.

# '**' 를 FS 로 사용. '*' 는 regex 문자이므로 일반 문자로 사용하기 위해서 escape
$ awk 'BEGIN{ FS="*\\*" } ...'

$ echo '111****333**444' | awk 'BEGIN{ FS="*\\*" } {print NF}'
4

$ echo '111****333**444' | awk 'BEGIN{ FS="**" } {print NF}'
3

awk 소스코드 외부에서 -v 옵션 등을 이용해 값을 설정할 때는 shell quotes 을 사용하는 것입니다. 이때는 설정되는 값이 그대로 awk 스트링 double quotes 안으로 전달된다고 생각하면 됩니다. 따라서 shell 변수를 사용해야 되는 경우가 아니면 single quotes 을 이용해 작성하는 것이 좋습니다.

# single quotes 을 이용하면 awk 소스코드 내에서 작성하는 것과 동일하게 작성할 수 있습니다.
$ awk -F '*\\*' 'BEGIN{...}' ...

$ awk -F '\t' 'BEGIN{...}' ...

$ awk -F '\\t' 'BEGIN{...}' ...
------------------------------------

# 만약에 double quotes 을 사용한다면 다음과 같이 해야 합니다.
# shell double quotes 에서도 \ 가 처리되므로
$ awk -F "*\\\\*" 'BEGIN{...}' ...

16 진수 형식 사용시 문제점

구 버전의 awk 에서 \xHH 16 진수 형식을 이용해 값을 출력할 때 주의할 점이 하나 있습니다. \xHH 뒤에 바로 붙여서 16 진수에 해당하는 문자( 0-9a-fA-F )가 오면 정상적으로 값이 출력 되지 않습니다.

참고로 gawk 4.2.1 버전에서는 문제가 해결되었다고 합니다.

# "K" 는 16 진수 문자에 해당되지 않으므로 정상적으로 출력된다.
$ awk 'BEGIN { print "\x41\x42\x43KKK" }'  | od -a
0000000   A   B   C   K   K   K  nl
0000007

# "F" 는 16 진수 문자에 해당되므로 정상적으로 값이 출력되지 않는다.
$ awk 'BEGIN { print "\x41\x42\x43FFF" }'  | od -a
0000000   A   B del  nl
0000004

shell 에서 echo, printf 명령을 사용할 때는 이와 같은 현상이 발생하지 않으므로 프로그램 버그나 오류인 줄 알았는데 POSIX 에서는 \xHH 형식을 이용한 16 진수 출력을 허용하지 않기 때문이라고 합니다.

$ awk --lint 'BEGIN { print "\x41\x42\x43FFF" }' | od -a
awk: cmd. line:1: warning: POSIX does not allow '\x' escapes
awk: cmd. line:1: warning: hex escape \x43FFF of 5 characters probably
not interpreted the way you expect
0000000   A   B del  nl
0000004

8 진수 형식의 경우는 이와 같은 문제가 발생하지 않으므로 8 진수 형식을 사용하거나 아니면 다음과 같이 분리해 사용하면 되겠습니다.

# 8 진수 형식의 경우는 그런 문제가 없다.
awk 'BEGIN { print "\101\102\103FFF" }'  | od -a
0000000   A   B   C   F   F   F  nl
0000007

# 또는 quotes 을 이용해 분리해 사용하면 된다.
$ awk 'BEGIN { print "\x41\x42\x43""FFF" }'  | od -a
0000000   A   B   C   F   F   F  nl
0000007