명령어라기보단 프로그래밍 언어로 보는게 맞다. 특정 파일 내 특정 텍스트에 대한 조작에 특화되어 있다. 스크립트 언어다.
개발에 참여한 Aho, Weinberger, Kernighan의 앞글자를 따서 만들어졌다.
기본 틀은 위의 documentation에 있는 내용이지만, 실제 implementation은 여러가지가 존재한다. gawk
, nawk
, mawk
가 대표적. 여기서는 대부분의 리눅스가 사용하는 gawk
를 기준으로 설명한다. (GNU implementation)
awk
에 대해 설명하기 전에 먼저 Record랑 Field에 대해서 설명을 해야 한다.
awk
의 특정 명령어 실행은 record 단위로 이루어진다. record를 나누는 기준인 character인 record separator을 토대로 record 개수가 정해지며 기본값은 \n
, 즉 입력값의 각 줄이 record라고 생각하면 된다. 이 값은 RS
값을 바꾸는 형태로 수정이 가능하다.
각 record는 또 field를 나누는 기준인 field seperator을 통해 또 나뉘어진다. 이것은 공백을 기준으로 나뉘어진다. \n
, \t
, 여러 공백도 하나의 공백으로 취급해서 나눈다.
각 field는 $
옆에 숫자로 접근이 가능하다. 첫번째 field는 $1
, 두번째는 $2
...의 형식. 전체 field
접근은 $0
으로 가능하다. 마지막 field의 경우 $NF
를 사용해서 접근하는 것도 된다.
밑은 예시.
PID TTY TIME CMD
23576 pts/3 00:00:00 bash
23804 pts/3 00:00:00 ps
|---| |---| |------| |--|
$1 $2 $3 $4 ($NF) --> fields
|--------------------------|
$0 --> record
awk
프로그램 기본 문법awk
program은 크게 규칙과 사용자정의 함수로 나뉘어진다.
규칙은 pattern과 action으로 이루어져있다. action은 여러 줄이 가능하며 ;
을 기준으로 나뉘어지고, {}
안에 전부 존재한다.
규칙은 해당 pattern을 만족하는 모든 record들에 대해 중괄호 안의 action을 수행하라는 것을 의미한다. 만약 pattern만 지정되고 action은 지정되지 않았으면 해당 pattern을 만족하는 모든 record들을 출력하라는 의미가 된다. 만약 action은 지정되어 있고 pattern이 지정되지 않았으면 모든 record들에 대해 해당 action을 수행하라는 의미가 된다.
타 언어처럼 조건문, 입출력 관련 문법, 그리고 그 외 예약어들이 존재를 한다. 대표적인건...
exit
: 프로그램 즉시 종료next
: continue
랑 유사하다. 현재 record에 대한 처리를 멈추고 다음 record의 처리 시작print
: 출력용 함수printf
: 출력용 함수. C언어의 printf
와 유사.주석은 #
사이의 모든 내용물이 해당되며, \
을 사용해 여러 줄을 하나의 줄로 나타내는게 가능하다.
awk
interpreter을 통해 바로 실행이 가능하다. 밑의 program
부분에 awk
program을 넣고 처리할 파일 (혹은 input stream)을 집어넣으면 된다. 이 때 프로그램 부분은 ''
으로 둘려쌓여야 한다. 쉘에서 이를 shell용 프로그램으로 생각해서는 안되기 때문.awk '(awk 프로그램)' (처리 대상 파일)
-f
를 사용한다.awk -f (awk 프로그램이 있는 파일) (처리 대상 파일)`
example1.txt
. 2023 KBO 최종 순위 관련 통계다.LG Twins 86 56 2 0.606 -
KT Wiz 79 62 3 0.560 6.5
SSG Landers 76 65 3 0.539 9.5
NC Dinos 75 67 2 0.528 11.0
Doosan Bears 74 68 2 0.521 12.0
KIA Tigers 73 69 2 0.514 13.0
Lotte Giants 68 76 0 0.472 19.0
Samsung Lions 61 82 1 0.427 25.5
Hanwha Eagles 58 80 6 0.420 26.0
Kiwwom Heroes 58 83 3 0.411 27.5
$ awk '{print}' example1.txt
LG Twins 86 56 2 0.606 -
KT Wiz 79 62 3 0.560 6.5
SSG Landers 76 65 3 0.539 9.5
NC Dinos 75 67 2 0.528 11.0
Doosan Bears 74 68 2 0.521 12.0
KIA Tigers 73 69 2 0.514 13.0
Lotte Giants 68 76 0 0.472 19.0
Samsung Lions 61 82 1 0.427 25.5
Hanwha Eagles 58 80 6 0.420 26.0
Kiwwom Heroes 58 83 3 0.411 27.5
$ awk '{print $3}' example1.txt
86
79
76
75
74
73
68
61
58
58
/
으로 regex pattern을 둘러싸면 된다. 밑은 팀 이름에 T가 있는 팀들(기아, LG, KT)의 승수를 출력한다.$ awk '/T/ {print $3}' example1.txt
86
79
73
~
을 넣고 그 다음에 사용할 regex pattern을 집어넣으면 된다. 예를 들어 팀 이름의 두번째 부분에 t
혹은 T
가 포함된 팀(기아, LG, 롯데)의 승수를 출력하고 싶으면 다음과 같이 한다.$ awk '$2 ~ /[tT]/ {print $3}' example1.txt
86
73
68
$ awk '$2 !~ /[tT]/ {print $3}' example1.txt
79
76
75
74
61
58
58
$ awk '$3 > 75 {print $1}' example1.txt
LG
KT
SSG
pattern1, pattern2
생각보다 빡치는 문법이다.
예를 들어 LG
를 포함하는 record부터 Doosan
을 포함하는 record까지의 승수를 출력하고 싶으면 다음과 같이 하면 된다. 첫 줄이 pattern1 만족을 하면서 출력, 이때 pattern2가 만족이 안되서 다음 record가 출력이 되고, 한동안 pattern1, pattern2가 맞지 않아서 계속 다음줄도 출력되다가 Doosan
을 만나면 즉각 종료된다.
$ awk '/LG/, /Doosan/ {print $3}' example1.txt
86
79
76
75
74
(1)
$ awk '$3 < 80, $3 >= 70 {print $1}' example1.txt
KT
SSG
NC
Doosan
KIA
Lotte
Samsung
Hanwha
Kiwwom
(2)
$ awk '$3 < 80, $3 < 70 {print $1}' example1.txt
KT
SSG
NC
Doosan
KIA
Lotte
Samsung
Hanwha
Kiwwom
(3)
$ awk '$3 < 70, $3 >= 70 {print $1}' example1.txt
Lotte
Samsung
Hanwha
Kiwwom
(4)
$ awk '$3 == 74, $3 <= 70 {print $1}' example1.txt
Doosan
KIA
Lotte
(5)
$ awk '$3 == 74, $3 >= 70 {print $1}' example1.txt
Doosan
(6)
$ awk '$3 == 74, $3 == 73 {print $1}' example1.txt
Doosan
KIA
위에 대해 하나하나 설명하자면
즉 range는 시작과 끝점을 확실하게 명시를 하는 형태의 특정 범위 안의 action 수행이라고 생각하면 된다. 간단히 말해, 6번처럼 쓰는 것만 올바르다고 봐야 한다. 그래서 pattern1,2에 부등호를 사용하는 것은 좋은게 아니라는 점 유의. 마치 python에서 range를 사용할 때의 시작점과 끝점에 범위를 집어넣는 느낌으로 프로그램을 만들려 하니 문제가 된다고 생각하면 된다.
BEGIN
: record를 본격적으로 처리하기 전에 수행하는 action들을 묶어넣는다.END
: record 처리가 완료된 후에 수행하는 action들을 묶어 넣는다.$ awk 'BEGIN {print "scoreboard"}; {print $3}; END {print "end of scoreboard"}' e
xample1.txt
scoreboard
86
79
76
75
74
73
68
61
58
58
end of scoreboard
$ awk '$3 < 80 && $3 >= 70 {print}' example1.txt
KT Wiz 79 62 3 0.560 6.5
SSG Landers 76 65 3 0.539 9.5
NC Dinos 75 67 2 0.528 11.0
Doosan Bears 74 68 2 0.521 12.0
KIA Tigers 73 69 2 0.514 13.0
NF
: record 안의 field 개수 접근에 활용 가능
NR
: record 개수 접근에 활용 가능
FILENAME
: 처리 중은 입력 파일 이름
FS
: field separator
RS
: record separator
OFS
: Output field separator. 출력물의 field를 어떻게 나눌지를 지정한다. 추후 보면 ,
을 기준으로 여러 출력물을 특정 character을 사이에 두고 출력하는데, 그때 사이에 들어갈 character이 뭔지를 지정한다. 기본은 ' '
.
ORS
: Output record separator. print 이후에 줄바꿈을 해야 할 때 사용하는 character을 지칭한다. 기본은 /n
. 출력물의 한 줄이 출력완료될때마다 이 값을 끝에 붙여넣는다.
file관련 변수들의 경우, 파일 처리 후에 제대로 값이 저장된다는 점 유의.
밑은 입력 파일의 줄개수 세는 방법.
$ awk 'END {print "File", FILENAME, "contains", NR, "lines."}' example1.txt
File example1.txt contains 10 lines.
$ awk 'BEGIN { FS = "." } { print $1 }' example1.txt
LG Twins 86 56 2 0
KT Wiz 79 62 3 0
SSG Landers 76 65 3 0
NC Dinos 75 67 2 0
Doosan Bears 74 68 2 0
KIA Tigers 73 69 2 0
Lotte Giants 68 76 0 0
Samsung Lions 61 82 1 0
Hanwha Eagles 58 80 6 0
Kiwwom Heroes 58 83 3 0
-F
로도 하는게 가능하다.$ awk -F "." '{ print $1 }' example1.txt
LG Twins 86 56 2 0
KT Wiz 79 62 3 0
SSG Landers 76 65 3 0
NC Dinos 75 67 2 0
Doosan Bears 74 68 2 0
KIA Tigers 73 69 2 0
Lotte Giants 68 76 0 0
Samsung Lions 61 82 1 0
Hanwha Eagles 58 80 6 0
Kiwwom Heroes 58 83 3 0
$ awk 'BEGIN {RS = "." } { print $1 }' example1.txt
LG
606
560
5
539
5
528
0
521
0
514
0
472
0
427
5
420
0
411
5
{}
안에 들어가며, ;
을 기준으로 구별이 된다.
변수 넣기, 산술, 증감, 조건문, 루프, 스위치, 출력용 함수들 등등이 가능하다. 어지간한 프로그램 언어 급의 표현능력을 가진다.
몇가지 예시를 밑에서 들겠다. 먼저 여러개를 공백을 두고 출력하는것은 ,
을 활용한다.
$ awk '{print $1, $5}' example1.txt
LG 2
KT 3
SSG 3
NC 2
Doosan 2
KIA 2
Lotte 0
Samsung 1
Hanwha 6
Kiwwom 3
,
을 빼자.$ awk '{print $1$5}' example1.txt
LG2
KT3
SSG3
NC2
Doosan2
KIA2
Lotte0
Samsung1
Hanwha6
Kiwwom3
""
을 활용하자.$ awk '{ print "first field:", $1}' example1.txt
first field: LG
first field: KT
first field: SSG
first field: NC
first field: Doosan
first field: KIA
first field: Lotte
first field: Samsung
first field: Hanwha
first field: Kiwwom
\n
등도 다 활용이 가능하다.$ awk 'BEGIN { print "one\ntwo\nthree"}' example1.txt
one
two
three
printf
를 활용해 C의 printf
와 유사한 형식으로 출력도 가능하다.$ awk '{ printf "%3d. %s\n", NR, $0 }' example1.txt
1. LG Twins 86 56 2 0.606 -
2. KT Wiz 79 62 3 0.560 6.5
3. SSG Landers 76 65 3 0.539 9.5
4. NC Dinos 75 67 2 0.528 11.0
5. Doosan Bears 74 68 2 0.521 12.0
6. KIA Tigers 73 69 2 0.514 13.0
7. Lotte Giants 68 76 0 0.472 19.0
8. Samsung Lions 61 82 1 0.427 25.5
9. Hanwha Eagles 58 80 6 0.420 26.0
10. Kiwwom Heroes 58 83 3 0.411 27.5
$ awk '{ sum += $5 } END { printf "number of draws : %d\n", sum/2 }' example1.txt
number of draws : 12
-f
)2가지 방법이 있다. awk
command를 사용하거나 shebang을 이용해 그냥 일반 명령어처럼 사용하는 것이 가능하다.
확장자는 .awk
로 해야 한다.
전자의 경우
$ cat examplepr.awk
BEGIN {
i = 1;
while (i < 6) {
print "Square of", i, "is", i*i;
++i;
}
}
$ awk -f examplepr.awk
Square of 1 is 1
Square of 2 is 4
Square of 3 is 9
Square of 4 is 16
Square of 5 is 25
$ cat examplepr.awk
#!/usr/bin/awk -f
BEGIN {
i = 1;
while (i < 6) {
print "Square of", i, "is", i*i;
++i;
}
}
$ ./examplepr.awk
Square of 1 is 1
Square of 2 is 4
Square of 3 is 9
Square of 4 is 16
Square of 5 is 25
-v
)awk
를 활용하는 경우가 자주 있을 것이다. 이 때, 쉘의 변수를 awk
에 전달해야 하는 경우도 분명 필요할 것이다. 여러 방법이 있으나 추천되는 방법은 해당 쉘 변수를 awk
의 변수로 직접 지정을 하는 것이다. 이 때 -v
를 활용한다.$ cat example.sh
read num
awk -v n="$num" 'BEGIN {print n}'
$ ./example.sh
31
31