본문 바로가기

프로그래밍(TA, AA)/스크립트

[스크립트] AWK

AWK와 SED는 텍스트 프로세싱 기능을 수행한다. 유닉스 프로그램들은 데이터를 일반 텍스트 파일로 저장하는 경우가 많기 때문에 유닉스 환경에서 AWK과 SED를 활용함으로써 처리할 수 있는 작업은 매우 다양한다. 게다가 파이프라인이 가능하여 표준 출력을 AWK와 SED 표준입력으로 받아 처리할 수 있다. 텍스트가 들어가는 모든 작업에 사용될 수 있다. 

 

AWK과 SED는 프로그래밍 언어이며, 어느정도 지식이 있어야 사용 가능하다. AWK과 SED는 perl 등장 이후로, 사용빈도가 급격히 줄었다. 왜냐하면 조금만 길어져도 스크립트가 너무 복잡해지기 대문이다. 그렇기 때문에 복잡한 프로그래밍으로는 거의 사용되지 않고 관용적인 one-liner(명령행 한줄로 쓰여져 동작하는 프로그램)로서 명맥을 이어가고 있다.

 

 


 AWK

pattern { action statements }
function name(parm list) { statements }

두번재 라인은 함수를 정의하는 문법이다. C와 다른점은 parm list에 매개변수 뿐만 아니라 로컬변수도 써줘야 한다는 점이다. 그점 외에는 특별한 것이 없다.

 

awk 프로세싱은 "입력 스트림 → awk processing → 출력 스트림"이다. 입력 스트림은 파일 또는 표준입력을 지정할 수 있다. 출력 스트림 역시 파일 또는 표준 출력이 될 수 있다. 그 중 awk processing 부분이 주목할 부분이며 첫라인에 나온 문법대로 프로그래밍 하면된다.

 

즉, 입력스트림을 받아서 pattern에 적용해보고 패턴에 일치하면 action statements를 실행하는 것이다. 또한 다수의 pattern{ action }을 체인으로 사용할 수 있다. 첫번째 라인을 C와 비슷한 모양으로 고친다면 아래처럼 나타낼 수 있다. pattern{ action }에서 pattern 부분을 생략하고, action의 if 문 안에 패턴을 검사하는 state를 넣는것이다.

{
  if(pattern) {
    action statements
  }
}

 

보다시피 pattern{ action }의 pattenr은 생략가능하다. 이 경우 모든 입력 스트림에 대해 {action}을 수행하게 된다. 그리고 pattern{ action }에서 { action }도 역시 생략 가능하다. 이 경우 pattern과 일치하는 입력 스트림 부분을 print하는 action이 자동으로 실행된다.

 

 

pre-defined variables

미리 정의된 변수들이 있다. built-in variables라고 부르기도 한다. 이 변수들을 이해하기 위한 몇가지 개념이 있다. rcord나 field는 awk의 입력으로 들어온 전체 텍스트 중 일부다. 전체 텍스트가 awk을 거치는 모습은 "Input → awk processing  output"와 같다. input은 표준입력이 될 수도 있고 평범한 파일일 수도 있는데 awk는 작업을 처리하기 위해서 input을 통째로 작업대 위에 올려놓지 않고 부분부분 잘라서 올려놓는데 이 때 작업대 위에 올려 놓은게 레코드다. 그리고 그 레코드는 또 필드로 이루어져 있다.

 

RS: record seperator (디폴트는 개행문자)
FS: field seperator (디폴트는 공백문자)
NF: the number of fields (FS로 나뉜 한 레코드 안의 필드 개수)
ORS: output record separator (디폴트는 개행문자)
NR: total number of record so far (현재 라인 번호)
ARGC, ARGV: 매개변수의 갯수와 매개변수를 가리키는 변수
FNR: number of record in the current input file
OFS: output field separator (디폴트는 공백문자)
FILENAME: name of input file
SUBSEP: separate multiple subscripts in array elements
IGNORECASE: 값이 0이 아니면 패턴 매칭에 대소문자 구별 안함

 

RS는 awk가 전체 텍스트 중 작업대 위로 레코드를 올릴때 어떤 단위로 올릴지 정하는 변수이다. 디폴트값은 newline 캐릭터이므로 이때 awk는 grep이나 sed처럼 한줄씩 끊어서 작업한다고 말할 수 있다.

 

FS는 레코드 내의 필드들의 구분자이다. 기본값은 공백이므로 어떤 래코드의 내용이 abc def ghi jkl이면 1번 필드($1)는 abc, 2번 필드($2)는 def 이런식이다. 그리고 $0은 abc def ghi jkl이다. 즉 현재 레코드의 전체 부분을 가리킨다.

 

awk의 매개변수에 파일을 여러개 지정했다면 FNR과 NR의 차이점도 알아야 한다. FNR은 지금 awk이 작업하고있는 파일 내에서 몇번째 레코드인지 가리키고 NR은 파일 구분없이 맨처음부터 지금까지 총 몇번재 레코드인지 가리킨다.

 

SUBSEP은 배열 사용시 필요한 내용이다.

 

1. awk 'NR%2==0{ print $0 }'
2. awk 'NR%2'
3. awk '0 '
4. awk '"0"'
5. awk 'NR>5&&NR<10'
6. awk "$0 = NR" "$0"

 

1번은 짝수줄 NR%2==0이면 출력하라 { print $0 }는 뜻이다.

2번은 홀수줄이면 출력하라는 뜻이다. 왜냐하면 NR이 홀수줄이면 NR%2가 0이 아니므로 드폴트 액션인 { print $0 }이 출력되기 때문이다. 여기서 NR%2의 값은 0 또는 1인데 0은 false를 뜻하고 0이 아닌 숫자는 true를 뜻한다.

3번은 아무 줄도 출력하지 않느다. 왜냐하면 항상 0, false이기 때문이다.

4번은 항상 출력한다. "0"은 문자열이기 때문에 항상 참이다.

5번은 6,7,8,9 줄을 출력한다.

6번은 줄 앞에 줄번호를 붙인다. $0은 현재 레코드인데 $0 = NR" "$0은 $0에 NR" "$0을 대입하라는 의미이다. 중간에 " "은 단지 줄번호(NR)과 레코드($0) 사이에 공간을 두기 위해 붙인 문자열에 지나지 않는다.

 

 

변수와 배열

변수는 숫자거나 스트링일 수 있다. awk의 변수와 배열은 따로 초기화하거나 타입을 지정할 필요가 없으며 필요시 자동으로 형변환이 된다. 즉 사칙연산이 필요할 때는 숫자였다가 문자열 이어붙이기를 할때는 스트링이 되기도 한다.

 

초기화가 필요없으므로 곧바로 a++; 이렇게 쓸 수도 있다. 이때 a의 값은 1이 된다. 왜냐하면 초기화하지 않는 변수의 값은 숫자의 경우 0이기 때문이다. 그리고 변수에 octal과 hexadecimal 값을 넣을 수 있다. a=0nn;, b=0xnn; 이렇게 사용하면 된다.

 

변수에 스트링을 대입하는 방법은 a="string" 이런식이다. 또한 스트링 안에 이스케이프 시퀀스도 사용할 수 있다. 

 

배열도 변수와 마찬가지로 초기화가 필요하지 않다. 다짜고짜 a[0]++; 이런식으로 쓸 수 있다. 그리고 특이하면서 유용하게 사용되는 특징은 배열의 인덱스에 스트링을 넣을 수 있다는 점이다. 그리고 숫자로는 음수나 소스를 넣을 수 있다. 또한 awk의 배열은 다차원 배열을 흉내낼 수도 있다.

 

즉 배열의 sub script를 여러 문자열이나 숫자의 조합 또는 변수의 조합으로 사용할 수 있으며 조합시에 ,를 사용하는데 이것은 아까 pre-defined 변수에서 SUBSEP 변수의 값인 캐릭터이다.

 

 

패턴

AWK의 syntax, awk 'pattern{ action statements }' 중에서 pattern 부분을 작성하는 방법은 다음과 같다.

BEGIN{ }: 모든 인풋보다 먼저 실행되는 statements를 지정, 즉 awk의 작업대에 첫 레코드가 올라가기 전에 실행되는 statement들의 블록을 지정하는 셈이다. 초기화 작업을 하는 곳이라고 생각하면 된다. BEGIN 패턴은 한 awk 명령에 여러번 나올 수 있는데 모든 BEGIN 블록은 한데 모아진다.
END{ }: 더이상 인풋이 없을때 실행되는 statements 지정. 역시 END블록은 여러번 나올 수 있고 하나의 END블록으로 모아진다.
/정규식/: //로 묶어서 정규식을 사용할 수 있다.
패턴1 && 패턴2: AND 논리 연산,
패턴1 || 패턴2: OR 논리 연산
!패턴: NOT 논리연산
패턴1, 패턴2: range pattern으로 패턴1인 레코드로부터 패턴2인 레코드까지인 범위를 지정한다.

 

 

awk 옵션들

-F fs: FS의 값을 지정한다. BEGIN{ FS="fs" } 이렇게 한것과 같다.
-v var=val: 변수 var을 val로 지정한다. BEGIN{var=val} 한것과 같다.
-f file: awk 스크립트를 지정한다.
--dump-variables[=file]: global변수들의 이름과 값을 출력한다. file을 지정하지 않으면 현재 디렉토리의 awkvars.out 파일로 출력된다. 디버깅용으로 사용할 수 있다.
--re-interval: 정규표현식 중 인터벌을 사용할 수 있게 한다.

 

 

https://rhdxmr.tistory.com/57