정의
AWK는 패턴 스캐닝언어로서 1977년에 Alfred Aho, Peter Weinberger, Brain Kerninghan에 의해 만들어진 language
뒷글자를 따서 AWK 로 지음
파일 수정이 용이하며 데이터베이스를 검색하고 변형하는데도 유용함
데이터 프로세싱, 리포트 작성, 간단한 데이터 베이스 구축등에 많이 응용함
파일의 각 라인에서 필드(field)를 인식할 수 있는 패턴 매칭 기능으로 조작하기 위함
파일로부터 레코드(record)를 선택하고, 선택된 레코드에 포함된 값을 조작하거나 데이터화하는 것을 목적
awk는 입력된 라인들의 데이터를 공백 또는 탭을 기준으로 분리해서 $1부터 각각의 필드로 분리해서 인식함
레코드와 필드라는 용어가 쓰임
레코드 = 해당 라인, row
필드 = 칼럼,col ($1, $2, $3, ...)
기본 포맷
awk [OPTION...] 'pattern { action }' [FILE | ARGUMENTS...]
pattern과 action이 모두 생략 가능하기 때문에 아래와 같은 형식도 쓰임
awk ['pattern'] file
=> action을 생략하는 경우 print라는 action이 default
awk '[action]' file
=> pattern을 생략하는 경우 모든 레코드에 대해 적용
예를 들면
# pattern 생략
$ awk '{ print }' ./file.txt # file.txt의 모든 레코드 출력
# action 생략
$ awk '/p/' ./file.txt # file.txt에서 p를 포함하는 레코드 출력
더 좋은 예를 들면
이런 계산을 할 수도 있다.
이런 파일을 만들고
1번째 필드와 2번째 필드를 곱한 값을 출력하는 것이다.
위에서 알 수 있듯이 필드를 공백(white space)으로 구분함
해석해보자면
:라는 구분자로 필드를 나눌 것이고
linux라는 패턴에 대해서 매칭을 시키고
1번 필드에 대한 print를 하겠다.
라는 말이다.
내부 변수
내장 함수
함수 | 설명 |
gsub(r, s) | 입력 문자열 전부에 걸쳐 정규식 표현 r을 문자열 s로 치환 |
gsub(r, s1, s2) | 입력 문자열 s2 에서 정규식 표현 r을 문자열 s1 으로 치환 |
index(s1, s2) | s1에서 s2의 위치를 넘겨준다. 없다면 0 |
length(arg) | 인자의 길이를 넘겨줌 |
match(s, r) | 문자열 s에서 정규식 표현 r과 매칭 되는 부분의 위치를 넘겨줌. |
split(string, array[, seperator]) | 구분자를 기준으로(기본:공백)해서 지정한 문자열을 배열로 만듬 |
sub(r, s) , sub(r, s1, s2) | gsub 와 동일 정규식 표현과 일치하는 문자열이 여러 개라도 처음 한 문자열만 치환 |
substr(s, m) | 문자열 s 에서 m번째 위치에서 끝까지 문자열을 넘겨줌 |
substr(s, m, n) | 문자열 s 에서 m번째부터 n번째까지 문자열을 넘겨줌 |
tolower(string) | 대문자를 소문자로 바꿈 |
toupper(string) | 소문자를 대문자로 바꿈 |
atan2(x, y) | 아크 탄젠트(역탄젠트) 값 |
cos(x) | cos 값 |
exp(x) | (자연 상수)e^x |
int(x) | 정수형으로 반환 |
log(x) | 로그 |
rand() | 0에서 1까지 랜점 |
sin(x) | sin 값 |
sqrt(x) | 제곱근 |
srand(expr) | 인자를 가진 난수(없으면 시간을 가지고 난수 발생) |
close(filename) | 지정한 파일을 닫음 |
close(cmd) | 지정한 명령어 파이프를 닫음 |
delete array[element] | 지정한 배열 요소를 지움 |
getline() | 다음 레코드를 읽음 |
getline [variable] [< "filename"] | 파일에서 읽음 |
next | 다음 레코드를 입력 받음 |
print [args] [> "filename"] | 인자를 출력 |
printf "format" [,exp] [> "filename"] | 형식에 맞춰 인자를 출력 |
sprintf(format [,exp]) | printf와 마찬가지로 사용되지만 값을 반환만 하고 출력은 하지 않음 |
system(cmd) | 시스템 내부 명령어를 실행 |
유의사항
- awk program의 command 문장은 single quotes(' ') 또는 double quotes(" ")로 둘러싸여 있음
- Input의 각 라인(Line)이 원하는 pattern과 일치하면, action 부분이 실행
- action 없이 pattern만 있는 경우, 원하는 pattern을 찾으면 각 Input 라인을 그대로 출력
- pattern 없이 action만 있는 경우, Input의 각 라인에 대해 action을 실행
용도
- 텍스트 파일의 전체 내용 출력
- 파일의 특정 필드만 출력
- 특정 필드에 문자열을 추가해서 출력
- 패턴이 포함된 레코드 출력
- 특정 필드에 연산 수행 결과 출력
- 필드 값 비교에 따라 레코드 출력
Option
옵션 | 설명 |
-u | 버퍼를 사용하지 않고 출력 |
-F{d} |
field separator 지정 d는 필드 사이를 구분하는 구분자 직접 지정하지 않으면 공백을 기준으로함 시스템 변수 FS를 지정하는 것과 같은 효과를 지님 **다중 필드 구분자 사용 가능함 |
-v 변수 = 값 | 스크립트를 실행하기 전에 미리 변수를 지정함 $를 쓰지 않고 변수 이름만 씀 C에서 #define처럼 생각하면 쉬움 |
-f {스크립트 파일} | 스크립트를 파일에서 가져온다 -f 옵션을 여러 번 사용하여 여러 개의 스크립트 파일을 동시에 불러와 지정한 파일에 적용할 수 있음 |
필드 구분자를 예로 설명하면
아래와 같은 식으로 쓸 수 있음
-F'[ :\t]' | 다중 필드구분자 ':'와 tab을 필드구분자로 사용 |
Action
액션은 {}로 둘러싸인 문장이며 세미콜론(;)으로 구분
패턴은 액션 앞에 위치함
액션은 간단한 문장 또는 복잡한 문장들의 그룹으로 만들 수 있음
awk '/test/{print "Hello, "$1""}' awkfile
패턴 액션 실행파일
awk -f
위 명령어를 이용하여 awk 명령들을 파일에 저장하고 파일에 입력된 명령을 사용하여 다른 파일을 처리할 수 있음
awk -f [awk명령파일] [awk명령을 적용할 텍스트파일]
vi awkcommand
{print "안녕하세요! " $1"님"}
{print $1, $2, $3, $4}
awk -f awkcommand /etc/passwd
awk와 같이 쓰는 정규 표현식
pattern 부분에 정규 표현식를 넣어서 라인을 매칭이 가능함
^ | 문자열의 시작과 매칭 |
$ | 끝과 매칭 |
. | 문자 한 개와 매칭 |
* | 문자가 없거나 그 이상 과 매칭 |
+ | 하나의 문자 또는 그 이상 과 매칭 |
- | 문자가 없거나 하나와 매칭 |
& | 검색된 문자열로 대체 할 때 사용 |
[swiftTFT:ABC] | 셋 중 하나의 문자와 매칭 |
[^ABC] | 셋 중 매칭 되는 문자 없음 |
[swiftTFT:A-Z] | 해당 범위 내 매칭 되는 문자가 있음 |
A|B | A또는B 문자 매칭 |
연산자 및 표현식
match 연산자
match연산자는 하나의 레코드 또는 필드 안에서 표현식과 매칭되는 것이 있는지 검사하는 연산자
~ 일치하는 부분
!~ 일치하지 않는 부분
$awk '$1 ~ /admin[1-9]/' pw
admin1 x 500 500 test /test /bin/csh
비교연산자
<, <=, ==, !=, >=, >
$awk '$3 == 500' pw
admin1 x 500 500 test /test /bin/csh
조건표현식
$awk '{max=($3 == $4) ? $1 : $2; print max}' pw
산술연산자
+-*/%^
논리연산자
&& || !
BEGIN - END
패턴에 맞는 입력을 식별하면 입력 데이터에 대한 처리가 진행되기 전 BEGIN에 정의된 액션을 실행함
모든 레코드(라인)에 대한 처리가 끝난 후 END에 정의된 액션을 실행함
일반적으로 아래의 형태를 가짐
- 시작(BEGIN) : 입력 데이터를 실행하기에 적합한 형태로 바꾸어주는 단계(전처리하는 부분)
- 실행(Routine) : BEGIN에서 잘 처리된(정규화된) 데이터를 실제 루틴으로 처리하는 것, 여기서 데이터는 처리 루틴에 따라 처리가 되며, 입력 값이 루틴을 거쳐 결과 값이 출력
- 끝(END) : BEGIN와 마찬가지로, 데이터가 처리된 후에 처리해야 할 내용들을 담고 있음, 결과의 추가 출력들을 예로 들 수 있음
위의 형태를 하고자 할 때 BEGIN - END 패턴을 쓴다.
예시
빌트인 내장변수(OFS, RS, FS등)들의 값을 변경하기 위해, 사용자정의형 변수들의 초기값을 할당하기 위해, 출력의 한부분으로서 헤더 또는 타이틀을 프린트하기위해 자주 사용
awk 'BEGIN{FS=":"; OFS="\t"; ORS="\n\n"}{print $1,$2,$3}' filename
#입력파일이 처리되기 전에 필드분리자(FS)가 콜론(:)으로 설정되고, 출력 필드 분리자(OFS)가 탭으로 설정되며, 출력 레코드 분리자(ORS)가 두개의 newline으로 설정됨
입력의 모든 라인이 처리되고 실행되며 뒤에 파일 명을 출력
awk 'END{print "records is " NR }' test.txt
스크립트를 정의해서 실행하게 된다면 아래와 같이 이용
sum.awk
#!/bin/awk
#
# This Program is for Summing of exam_result.
#
# BEGIN : 프로그램 시작 처리
BEGIN {
sum = 0;
print "총점 출력 프로그램";
}
# ROUTINE : 프로그램 본문
{
sum += $2;
}
# END : 프로그램 마무리 처리
END {
print "합계 : " sum;
average = sum / NR;
print "평균 : " average;
}
############################################
score.txt
100
80
99
66
87
99
간단한 실행을 하고자 하면 아래처럼 이용도 가능
awk 'BEGIN {print "TITLE : Field value 1,2"} {print $1,$2} END {print "Have Finished"} test.txt
시작 전에 실행할 action에 대해 설명해주고
action을 실행하고
action이 끝났음을 알려주자
제어문
실제로 제어문을 한 줄로 쓰는 것은 가독성이 엄청나게 떨어진다.
그래서 awk는 개행 문법을 지원함
→ 커맨드 라인 한 줄 이상으로 작성하여 하나의 커맨드 실행 가능
=> 아래와 같이 실행 가능
# if문
if ( condition ) { Routine } else { Routine }
# for문
for ( init ; condition ; re ) { Routine }
# while문
while (condition) { Routine }
# do ~ while문
do { Routine } while (condition)
# 반목문 제어
break
continue
return
# 프로그램 제어
next
exit
#if
$ awk '{
total=$3+$4+$5;
avg=total/3;
if ( avg >= 90 )
grade="A";
else if ( avg >= 80)
grade ="B";
else if (avg >= 70)
grade ="C";
else
grade="D";
print $0, grade;
}' filename
#for
$ awk '{
for(i=0;i<2;i++)
print( "for loop :" i "\t" $1, $2, $3)
}' filename
#while
$ awk '{i=1;
while(i<=NF) {
print NF, $1; i++}
}' filename
break문이나 continue 문과 같은 제어도 가능
$ awk '{
for(x=3; x<=NF; x++)
if($x<0) {print "Bottomed out!"; break}
for(x=3; x<=NF; x++)
if($x==0) {print "Get next item"; continue}
}' filename
프로세스 제어도 가능
next : 입력 파일로부터 다음 입력행을 가져와서 awk 스크립트의 맨 처음부터 다시 실행
exit : awk 프로그램을 종료시킬 때 사용. exit 문은 레코드의 처리를 중단시키지만, END 문 너머로 건너뛰지 않음
# 1번째 필드가 Peter를 포함하면 이 행을 지나고 다음행을 입력받고, 스크립트는 처음부터 다시 실행
$ awk '{
if($1~/Peter/) {next}
else {print}
}' filename
# 첫 번째 레코드만 출력하고 실행 중지
$ awk '{ print $0; exit }' file.txt
예시
#/etc/passwd파일에서 계정명(ID)만 출력
#1. 파일의 형태를 보기
cat /etc/passwd
#2. 계정명 : 패스워드부분 : UID : GID : 코멘트 : 홈디렉토리 : 기본사용쉘 필드로 나눠짐
root:x:0:0:root:/root:/bin/bash
#3. $1 $2 $3 $4 $5 $6 $7
#4. 원하는 필드 출력
cat /etc/passwd | awk -F: '{print $1}'
root
###########################################################################################
# /home을 home 디렉토리로 사용하는 모든 사용자 정보 출력하기(명령어 파일로 만들어서 한 번에 실행하기)
#커맨드 해석
grep home passwd | awk -F: '{print "grep "$1" /home/moonly/passwd"}' > test.sh
#grep home passwd passwd파일내에서 home이라는 문자를 가진 행들은 모두 출력
#awk -F: '{print "grep "$1" /home/moonly/passwd"}' grep :기준으로 첫번째문자 /home/moonly/passwd => 출력
#> test.sh test.sh파일로 문자열 저장
#test.sh 파일은 아래와 같이 작성됨
grep wnn /home/test/passwd
grep mysql /home/test/passwd
grep pcpuser /home/test/passwd
여기서
#!/bin/bash => 쉘스크립트 실행을 위해 추가
chmod 700 test.sh
#./test.sh 실행
#test.sh파일에 저장되어있던 grep명령이 실행됨
###################################################################################################
#각 행 번호 출력 (NR 내장 변수 이용)
$ cat test.csv
1,2,3
4,5,6
7,8,9
$ cat test.csv | awk -F, '{print NR " " $0;}'
1 1,2,3
2 4,5,6
3 7,8,9
$ awk_example]$ cat test.csv | awk -F, '{print NR-1 " " $0;}' # 0부터 시작
0 1,2,3
1 4,5,6
2 7,8,9
#######################################################################################################
#패턴 매칭
$ awk '/pp/' ./file.txt # "pp" 가 포함된 레코드만 유효
$ awk '/[2-3]0/' ./file.txt # 20, 30 이 포함된 레코드만 유효
$ awk '$1 == 2 { print $2 }' ./file.txt # 첫 번째 필드가 2인 레코드의 두 번째 필드 출력
$ awk '$3 > 70 { print $0 }' ./file.txt # 세 번째 필드가 70보다 큰 레코드 출력
$ awk '$3 == 30 && $4 ==40 { print $2 }' file.txt # 세 번째 필드가 30이고 네 번째 필드가 40인 레코드의 두 번째 필드 출력
$ cat c.txt | awk '$1=="a"' str.txt # 특정 레코드만 출력
$ cat c.txt | awk '$1!="a"' str.txt # 특정 레코드 제외 출력
$ cat c.txt | awk '$2~/^e/' str.txt # 패턴비교. $2필드가 문자e 로 시작하는 레코드만 출력
참고링크
https://recipes4dev.tistory.com/171