GNU Awk 사용자 가이드/시작하기

위키책, 위키책

awk의 기본적인 기능은 파일들에서 특정 패턴을 포함하는 행들(이나 텍스트의 어떤 단위들)을 찾는 것이다. 어떤 한 행이 패턴에 맞다면, awk는 그 행에 대해서 특정한 동작을 수행한다. awk는 입력 행들을 입력 파일의 끝에 다다를 때까지 이런 식으로 계속 수행한다.

awk의 프로그램은 다른 언어의 프로그램과는 달리 데이터 기반 프로그램이다 (즉, 작업하고자 하는 데이터와 그 데이터를 찾았을 때 수행할 작업을 설명할 수 있다). 다른 대부분의 언어는 절차형이다. 다시 말해, 프로그램이 수행해야 할 모든 단계를 매우 자세하게 설명해야 한다는 뜻이다. 절차형 언어로 작업하면, 그 프로그램이 처리할 데이터를 명확히 설명하기가 매우 힘들다. 이런 이유에서 awk 프로그램은 읽고 쓰기가 매우 쉽다.

awk를 실행시키려면, awk가 무엇을 해야 할 지를 나타내는 awk 프로그램을 지정해야 한다. 프로그램은 일련의 규칙들로 이뤄져 있다(그중에는 고급 기능인 함수의 정의가 있을 수 있지만 지금은 무시한다. 자세한 내용은 사용자 정의 문서를 보라). 각각의 규칙은 탐색할 패턴과 패턴을 찾은 뒤 수행할 동작으로 이루어져 있다.

구문적으로, 한 규칙은 패턴 뒤에 동작이 뒤따르는 형식으로 이루어져 있다. 동작은 패턴과 분리하기 위해 중괄호로 싸여 있다. 일반적으로 규칙을 구분할 때는 개행을 사용한다. 따라서 awk 프로그램은 다음과 같은 형태를 띈다:

패턴 { 동작 }
패턴 { 동작 }
…

awk 프로그램 실행 방법[+/-]

awk 프로그램을 실행하는 방법에는 여러가지가 있다. 만약 프로그램의 길이가 짧다면, 가장 쉬운 방법은 다음과 같이 awk를 실행하는 명령어에 넣는 것이다:

awk 'program' input-file1 input-file2 …

프로그램이 길 경우에는, 일반적으로 파일로 저장해서 다음과 같이 명령어로 실행하는 것이 더 편리하다:

awk -f program-file input-file1 input-file2 …

이 문단에서는 두 메커니즘을 각각의 변형들과 함께 설명한다.

일회성 awk 프로그램[+/-]

awk에 익숙하다면, 종종 사용하고 싶을 때 마다 간단한 프로그램을 적을 것이다. 그러고 나면 첫 번째 인자로 awk 명령어를 넣어서 다음과 같이 실행할 수 있다:

awk 'program' input-file1 input-file2 …

프로그램은 앞에서 설명했듯이 일련의 패턴과 동작으로 이루어져 있다.

이 명령어 형식은 awk를 실행하고 입력 파일의 기록을 처리하는 프로그램을 사용하기 위해 셸이나, 명령 인터프리터에서 지시한다. 프로그램 주변에는 작은 따옴표가 있어서 셸이 awk 문자를 셸 특수 문자로 인식하지 않도록 한다. 또한 따옴표는 셸이 모든 프로그램을 awk의 단일 인자로 다루게 만들어서 프로그램이 여러 줄로 구성될 수 있게 한다.

이 형식은 짧거나 중간 정도 크기의 awk 프로그램을 셸 스크립트에서 실행하는데 있어서 유용하다. 왜냐하면 awk 프로그램을 위한 독자적인 파일이 필요 없기 때문이다. 독립적인 셸 스크립트가 더 신뢰할 수 있는데, 그 이유는 잘못 배치될 다른 파일이 없기 때문이다.

나중에 이 단원의 매우 단순한 예제에서 짧은 독자적인 프로그램을 일부 볼 것이다.

awk를 입력 파일 없이 실행하기[+/-]

awk를 입력 파일 없이도 실행할 수 있다. 다음의 명령어를 입력하면:

awk 'program'

awk는 프로그램을 표준 입력에 적용해, 키보드로 입력하는 모든 것을 받아들인다. 이는 Ctrl-d로 파일의 끝, EOF를 지시할 때 까지 계속된다. (POSIX 연산 시스템이 아니면 EOF 문자는 다르게 정의되어 있을 수 있다.)

그 예시로 다음 프로그램은 컴퓨터 프로그래밍의 복잡성에 대해 걱정하지 않을 수 있도록 친절한 조언을 출력한다 (더글라스 아담스의 은하수를 여행하는 히치하이커를 위한 안내서에서):

$ awk 'BEGIN { print "Don\47t Panic!" }'
-| Don't Panic!

awk 는 BEGIN과 관련된 문을 어떤 입력을 읽기 전에 수행한다. 만약 이 경우처럼 프로그램에 다른 문이 없다면, awk는 처리하지도 않을 입력을 읽으려고 하는 것이 아니라 그대로 종료한다. ‘\47’은 작은 따옴표를 안 예쁜 셸 인용 트릭을 사용하지 않고서도 프로그램에 넣는 마법적인 방법이다 (차후에 설명할 것이다).

참고: 배시를 셸로 사용한다면, 프로그램을 상호작용적으로 프로그램을 실행하기 전에 ‘!’를 특수 문자로 처리하는 C 셸 스타일 명령 기록을 비활성화 하기 위해 명령어 ‘set +H’를 실행해야 한다. 이 명령을 개인 스타트업 파일에 넣어 두는 것을 추천한다.

다음의 단순한 awk 프로그램은 키보드 입력을 모두 표준 출력으로 복사하는 cat 유틸리티를 에뮬레이트한다(이 프로그램이 작동하는 원리는 나중에 간략히 설명할 것이다):

$ awk '{ print }'
Now is the time for all good men
-| Now is the time for all good men
to come to the aid of their country.
-| to come to the aid of their country.
Four score and seven years ago, ...
-| Four score and seven years ago, ...
What, me worry?
-| What, me worry?
Ctrl-d

긴 프로그램 실행하기[+/-]

종종 awk 프로그램은 매우 길어질 수 있다. 이런 경우, 프로그램을 독립적인 파일에 두는 것이 더 편리하다. awk에 프로그램으로 파일을 쓸 것이라는 것을 전해주기 위해, 다음과 같이 입력하면 된다:

awk -f source-file input-file1 input-file2 …

-f는 awk 유틸리티에게 awk 프로그램을 source-file에서 사용할 것이라는 것을 지시한다(설정 참고). 어떤 파일 이름이든지 source-file로 쓸 수 있다. 예를 들면, 다음 프로그램:

BEGIN { print "Don't Panic!" }

을 advice 파일에 넣어서 다음 명령어:

awk -f advice

를 실행하면 다음과 같은 동작을 한다:

awk 'BEGIN { print "Don\47t Panic!" }'

이 명령어는 이미 설명했다(터미널 입력 참고). -f로 지정한 파일 이름 주변에는 보통 작은 따옴표를 쓰지 않아도 된다. 왜냐하면 파일 이름의 대부분은 셸의 특수 문자를 포함하지 않기 때문이다. advice에서 awk 프로그램은 작은 따옴표로 두르지 않은 것을 주목하라. 따옴표는 awk 명령 행에서 쓰일 때만 필요하다. (또한, 프로그램을 파일에 저장해서 사용할 경우에는 마법의 ‘\47’을 쓰지 않고서도 작은 따옴표를 바로 쓸 수 있다.)

만약 이런 awk 프로그램 파일을 지정하고 싶다면, 파일 이름에 .awk 확장자를 붙여둘 수 있다. 이는 awk 프로그램의 실행에 영향을 주지는 않지만, 이것이 awk 프로그램이라는 것을 알아보기는 쉽다.

실행 가능한 awk 프로그램[+/-]

awk를 배우고 나면, 독립적인 awk 스크립트를 짜고 싶어질 수 있다, 이는 ‘#!’ 스크립트 메커니즘을 활용해서 만들 수 있다. 이를 많은 시스템에서도 할 수 있다.[1] 예를 들어 advice 파일을 다음처럼 업데이트 할 수 있다:

#! /bin/awk -f

BEGIN { print "Don't Panic!" }

이 파일을 (chmod 유틸리티로)실행 가능하게 만들고 나면, 셸에 단순히 ‘advice’라고 치기만 하면 시스템은 이를 ‘awk -f advice’를 입력한 것처럼 awk를 실행한다:

$ chmod +x advice
$ advice
-| Don't Panic!

(여기서 셸의 탐색 경로 변수 [특히 $PATH]에 현재 경로가 있다고 가정했다. 그렇지 않으면 셸에 ‘./advice’라고 입력해야 한다.)

독립적 awk 스크립트는 프로그램이 awk로 만들어 졌다는 것을 모르는 사람들도 쓸 수 있는 프로그램을 만들 때 유용하다.

‘#!’ 이해하기
awk는 인터프리트 언어이다. 이는 awk 유틸리티가 프로그램을 읽어서 프로그램의 지시를 따라 데이터를 처리한다는 것을 의미한다. (이 부분은 C와 같이 프로그램이 시스템의 프로세서가 바로 실행할 수 있는 기계어로 컴파일 되는 컴파일 언어와는 다르다.) awk 유틸리티는 따라서 인터프리터라고 불린다. 많은 현대 언어는 인터프리트 언어이다.

‘#!’으로 시작하는 행은 실행할 인터프리터의 전체 파일 이름과 인터프리터로 전달할 단일 선택 초기 명령 행 인자를 나열한다. 연산 시스템은 그러고 나서 인터프리터를 주어진 인자와 실행한 프로그램의 전체 인자로 실행한다. 목록의 첫 번째 인자는 awk 프로그램의 전체 이름이다. 나머지 인자 목록은 awk의 다른 설정이나, 데이터 파일, 또는 둘 다 포함한다. (많은 시스템에서 awk를 /bin이 아니라 /usr/bin에서 찾을 수 있다는 점을 주목하라.)

어떤 시스템은 인터프리터의 이름을 32문자로 제한한다. 이는 종종 상징적 링크를 이용해서 다뤄진다.

‘#!’행에서 awk의 경로 다음에 인자를 하나보다 많이 둘 수는 없다. 연산 시스템은 행의 나머지 부분을 하나의 인자로 다루고 awk로 넘기기 때문이다. 여러 인자를 사용했을 경우에는 혼란스러운 행동이 나타날 수 있다 - 대부분은 awk의 어떤 사용 진단이다.

마지막으로, ARGV[0](빌트인 변수 참고)의 값은 연산 시스템에 따라 달라진다. 어떤 시스템은 그 자리에 ‘awk’를 넣고, 어떤 시스템은 (/bin/awk 같이) awk의 전체 경로를 넣고, 어떤 시스템은 스크립트의 이름(‘advice’)을 넣는다. (d.c.) 스크립트의 이름을 제공하기 위해서 ARGV[0] 값에 의존하면 안 된다.

awk 프로그램에서 주석[+/-]

주석은 읽는 사람을 위한 프로그램에 포함된 텍스트로, 프로그램에서 실행 가능하지 않다. 주석은 프로그램이 어떤 동작을 하는 지, 어떻게 동작하는 지를 설명한다. 주석이 없으면 이해하기가 어렵기 때문에 거의 모든 프로그래밍 언어는 주석을 제공한다.

awk 언어에서는, 주석은 숫자 기호 문자 (‘#’)로 시작해서 행의 끝까지 계속된다. ‘#’은 행의 첫 번째 문자여야 할 필요는 없다. awk 언어는 행에서 숫자 기호 이후의 모든 것을 무시한다. 예를 들어, 다음을 advice에 넣을 수 있다:

# This program prints a nice, friendly message.  It helps
# keep novice users from being afraid of the computer.
BEGIN    { print "Don't Panic!" }

주석을 일회성 awk 프로그램에도 넣을 수도 있지만, 그다지 유용하지는 않다. 왜냐하면 주석의 목적은 다른 사람이 나중에 프로그램을 읽게 되었을 때, 이해하는 것을 돕는 것이기 때문이다.

주의: 일회성 프로그램에서 언급 했듯이, 셸 스크립트를 독립적으로 두기 위해서 어느 정도 짧은 프로그램을 작은 따옴표로 둘러서 쓸 수 있다. 그런 경우에는 주석(이나 프로그램 내의 모든 곳)에서 어퍼스트로피(즉, 작은 따옴표 한 개)를 사용하면 안된다. 셸을 따옴표를 전체 프로그램을 닫는 따옴표로 해석할 것이다. 결과적으로, 보통 셸은 짝이 맞지 않는 따옴표에 대한 메세지를 출력하고, awk가 실제로 작동한다면, 구문 오류에 관한 이상한 메세지를 출력할 것이다. 그 예시로 다음을 보자:
$ awk 'BEGIN { print "hello" } # let's be cute'
>
이 셸은 처음 두 따옴표가 짝이 맞고, 새로운 따옴표 문자열이 명령 행의 끝에서 시작되는 것으로 보인다. 따라서 이 명령어는 이차 프롬포트로 입력을 더 받으려고 기다린다. 유닉스 awk에서, 따옴표로 두른 문자열을 닫으면 다음과 같은 결과가 나온다:
$ awk '{ print "hello" } # let's be cute'
> '
error→ awk: can't open file be
error→  source line number 1
작은 따옴표 전에 역슬래시를 두는 것은 의미가 없다. 왜냐하면 작은 따옴표 안에서 역슬래시는 특수 문자가 아니기 때문이다. 다음 문단은 셸의 인용 규칙을 설명한다.

셸 인용 문제[+/-]

DOS 인용: 윈도우 배치 파일에서 인용하기. 어느 정도 짧은 AWK 프로그램에서, 프로그램을 awk 명령 행에 프로그램을 입력하는 것이 가장 편리하다. 전체 프로그램을 작은 따옴표로 두르는 것이 가장 좋다. 이는 프로그램을 셸에서 바로 입력하던, 큰 셸 스크립트의 일부로 쓰던 간에 관계 없이 사실이다:

awk 'program text' input-file1 input-file2 …

한번이라도 셸에서 작업하게 되면, 셸의 기본 인용 규칙의 기본 지식을 익혀두는 것이 도움이 된다. 다음의 규칙은 POSIX 보완, Bourne 스타일 셸에만 적용된다(그 중에는 배시, GNU Bourne-Again 셸이 있다). C 셸을 사용하고 있다면, 다음 내용은 도움이 되지 않는다.

규칙으로 바로 들어가기 전에, 이 웹 페이지에 나타나는 널, 또는 빈 문자열의 개념들을 소개할 것이다.

널 문자열은 값이 없는 문자 데이터이다. 다시 말하자면, 비어있다. awk 프로그램에서는 이렇게 쓴다: "". 셸에서는 작은 따옴표나 큰 따옴표로 ""나 처럼 쓸 수 있다. 비록 널 문자열은 문자는 없지만, 분명히 존재한다. 예를 들어 다음 명령어를 보자:

$ echo ""

여기서 echo 유틸리티는 문자가 없는 단일 인자를 받는다. 이 웹의 나머지는 널 문자열과 빈 문자열을 바꿔쓸 수 있다. 이제부터 인용 규칙으로 들어간다:

  • 인용된 문자열은 인용되지 않은 것을 인용된 문자열처럼 연결할 수 있다. 셸은 모든 것을 명령어의 한 인자로 바꾼다.
  • 어떤 문자 앞에 역슬래시(‘\’)를 쓰면 그 문자를 인용한다. 셸은 역슬래시 문자를 지우고 명령어의 인용된 문자를 건너뛴다.
  • 작은 따옴표는 인용된 모든 문자를 보호한다. 셸은 인용된 텍스트에 대해서 해석을 하지 않고, 쓰여진 그대로 명령어에 전달한다. 작은 따옴표를 작은 따옴표로 인용된 텍스트 내에서 쓰는 것은 불가능하다. 시도할 경우 어떤 일이 일어나는 지에 대해서는 주석으로 돌아가라.
  • 큰 따옴표는 인용된 대부분의 문자를 보호한다. 셸은 인용된 텍스트에 대해서 적어도 변수와 명령어 치환을 한다. 다른 셸은 큰 따옴표로 인용된 텍스트에 추가적인 처리를 할 수 도 있다.
  • 큰 따옴표로 인용된 텍스트에 있는 특정 문자는 셸에서 처리될 수 있으므로 이스케이프 문자로 처리해야 한다. 주의해야 할 문자는 ‘$’, ‘`’, ‘\’, ‘"’로, 모두 큰 따옴표로 인용된 텍스트 내에서 프로그램에 그 문자를 프로그램에 전달하기 위해서는 역슬래시를 앞에 써야 한다. (선행하는 역슬래시는 지워진다.) 따라서, 터미널 입력에서 본 예시:
awk 'BEGIN { print "Don\47t Panic!" }'
는 다음과 같이 쓸 수 있다:
$ awk "BEGIN { print \"Don't Panic!\" }"
-| Don't Panic!
작은 따옴표는 큰 따옴표 안에 있을 때는 특수 문자가 아닌 것을 주목하라.
  • 널 문자열은 널 문자열이 아닌 명령행의 인자 중 일부로 나타날 때 제거되나, 명시적인 널 오브젝트는 유지된다. 예를 들어, 필드 구분자 FS는 널 문자열이여야 한다는 것을 지정하기 위해서, 다음과 같이 사용할 수 는 있으나:
awk -F "" 'program' files # correct
다음과 같이 사용할 수는 없다:
awk -F"" 'program' files  # wrong!
후자의 경우, awk는 프로그램의 텍스트를 FS 값으로, 첫 번째 파일의 이름을 프로그램 텍스트로 이용하려 시도할 것이다! 이 경우 최선의 경우에는 구문 오류를 일으키며, 최악의 경우에는 알 수 없는 동작을 할 것이다.

작은 따옴표와 큰 따옴표를 혼용하는 것은 어렵다. 따라서 다음과 같은 셸 인용 트릭에 의존해야 한다:

$ awk 'BEGIN { print "Here is a single quote <'"'"'>" }'
-| Here is a single quote <'>

이 프로그램은 세 인용된 문자열 이 연결된 형태로 이루어져 있다. 첫 번째와 세 번째는 작은 따옴표로 인용되었고, 두 번째는 큰 따옴표로 인용되었다.

이는 다음과 같이 “줄여”쓸 수 있다:

$ awk 'BEGIN { print "Here is a single quote <'\''>" }'
-| Here is a single quote <'>

둘 중 어떤 것이 읽기 편할 지는 스스로 판단하라.

다른 선택지는 큰 따옴표를 사용해서 안에 포함된 awk 수준의 인용을 탈출시키는 것이다:

$ awk "BEGIN { print \"Here is a single quote <'>\" }"
-| Here is a single quote <'>

이 선택은 더 고급 awk 프로그램이 큰 따옴표, 역슬래시, 통화 기호가 매우 빈번하게 나타나기 때문에 고통스러울 것이다.

세 번째 선택지는 다음과 같이 작은 따옴표나 큰 따옴표에 대응하는 팔진법 이스케이프 시퀀스를 사용하는 것이다(이스케이프 시퀀스 참고):

$ awk 'BEGIN { print "Here is a single quote <\47>" }'
-| Here is a single quote <'>
$ awk 'BEGIN { print "Here is a double quote <\42>" }'
-| Here is a double quote <">

이 방법은 잘 작동하지만, 이스케이프한 것이 무엇인지 주석을 달아 둬야 한다.

네 번째 선택지는 다음과 같이 명령 행 변수 할당을 이용하는 것이다:

$ awk -v sq="'" 'BEGIN { print "Here is a single quote <" sq ">" }'
-| Here is a single quote <'>

(여기서 두 문자열 상수와 sq의 값은 print에 의해 출력되는 하나의 문자열로 연결된다.)

작은 따옴표와 큰 따옴표 둘 다 awk 프로그램에서 필요로 한다면, 셸의 영향을 받지 않는분리된 파일로 옮기는 것이 최선이다.

MS 윈도우 배치 파일에서 인용[+/-]

이 웹 페이지는 POSIX 시스템과 POSIX 셸에 대해서 다루지만, 다음 문제는 많은 사용자에게 충분히 일어나기 때문에 설명할 만한 가치가 있다.

마이크로소프트 윈도우 시스템의 “셸”은 인용할 때 큰 따옴표를 써서, 명령 행 스크립트에서 이스케이프 된 큰 따옴표 문자를 사용하는 것이 어렵거나 불가능 하다. 다음 예시는 Jeroen Brink의 의례 대로, 파일에 있는 모든 행을 큰 따옴표로 인용한 형태로 출력하는 한 줄 짜리 스크립트에서 큰 따옴표를 이스케이프 시키는 방법을 나타낸다:

{ print "\"" $0 "\"" }

MS 윈도우 명령 행에서 위의 스크립트는 다음과 같이 쓰면 된다:

gawk "{ print \"\042\" $0 \"\042\" }" file

이 예시에서 ‘\042’는 큰 따옴표로 인용되었을 때의 팔진법 코드이다. gawk는 이를 print 문의 출력에서 큰 따옴표로 바꾼다.

MS 윈도우 이스케이프에서, 큰 따옴표는 이스케이프 할 때 역슬래시를 쓰지만, 역슬래시 자신은 일반적으로 이스케이프 되지 않기 때문에 까다롭다. 역슬래시가 복제되는지 아닌지는 그 뒤에 큰 따옴표가 있는지에 달려있다. MS 윈도우의 큰 따옴표로 인용된 문자열은 다음과 같은 규칙을 따른다:

  1. 원 문자열에 있는 모든 큰 따옴표에 대해서, N을 큰 따옴표 바로 앞에 있는 역슬래시의 개수라고 하고 N은 0이 될 수도 있다. 이 역슬래시 N개를 2*N+1개로 바꾼다
  2. N을 원 문자열에 있는 연속하는 역슬래시의 개수라고 하고 N은 0이 될 수도 있다. 이 역슬래시 N개를 2*N개로 바꾼다
  3. 그 결과로 나타난 문자열을 큰 따옴표로 인용한다.

따라서 이전 예시의 큰 따옴표로 인용된 한 줄 짜리 스크립트 ‘{ print "\"" $0 "\"" }’는 다음과 같이 쓸 수 있다:

gawk "{ print \"\\\"\" $0 \"\\\"\" }" file

하지만, ‘\\\"’대신에 ‘\042’를 쓸 수 있으며, 읽기에도 쉽다. 왜냐하면 큰 따옴표가 뒤에 있지 않는 역슬래시는 복제할 필요가 없기 때문이다.

예제를 위한 데이터 파일[+/-]

이 웹 페이지의 많은 예시들은 두 개의 샘플 데이터 파일에서 입력을 받는다. 첫 번째는 메일 목록(mail-list)으로, 사람들의 이름을 이메일 주소와 그 사람에 대한 정보의 목록을 나타낸다. 두 번째 데이터 파일은 재고 출하량(inventory-shipped)으로 월별 출하량의 정보를 포함한다. 두 파일에서, 각각의 행은 하나의 기록으로 다뤄진다.

메일 목록에서, 각각의 기록은 그 사람의 이름과, 전화번호, 이메일 주소, 그리고 저자와의 관계에 대한 코드로 이루어져 있다. 열은 공백 문자로 정렬되어 있다. 마지막 열의 ‘A’는 그 사람이 아는 사람(acquaintance)이라는 것을 나타낸다. 마지막 열의 ‘F’는 그 사람이 친구(friend)임을 나타낸다. ‘R’은 친척(relative)을 의미한다:

Amelia       555-5553     amelia.zodiacusque@gmail.com    F
Anthony      555-3412     anthony.asserturo@hotmail.com   A
Becky        555-7685     becky.algebrarum@gmail.com      A
Bill         555-1675     bill.drowning@hotmail.com       A
Broderick    555-0542     broderick.aliquotiens@yahoo.com R
Camilla      555-2912     camilla.infusarum@skynet.be     R
Fabius       555-1234     fabius.undevicesimus@ucb.edu    F
Julie        555-6699     julie.perscrutabor@skeeve.com   F
Martin       555-6480     martin.codicibus@hotmail.com    A
Samuel       555-3430     samuel.lanceolis@shu.edu        A
Jean-Paul    555-2127     jeanpaul.campanorum@nyu.edu     R

데이터 파일 "재고 출하량"은 그 해의 출하량을 나타낸다. 각각의 기록은 달과, 출하된 초록색 금고의 개수, 빨간 상자의 개수, 주황색 가방의 개수, 그리고 파란 소포의 개수이다. 전체 16개의 항목으로, 작년 12달과 올해 4달을 다룬다. 빈 줄은 두 해의 데이터를 나눈다:

Jan  13  25  15 115
Feb  15  32  24 226
Mar  15  24  34 228
Apr  31  52  63 420
May  16  34  29 208
Jun  31  42  75 492
Jul  24  34  67 436
Aug  15  34  47 316
Sep  13  55  37 277
Oct  29  54  68 525
Nov  20  87  82 577
Dec  17  35  61 401

Jan  21  36  64 620
Feb  26  58  80 652
Mar  24  75  70 495
Apr  21  70  74 514

샘플 파일은 gawk 배포 파일의 경로 awklib/eg/data에 포함되어 있다.

매우 단순한 예제[+/-]

다음의 명령어는 메일 목록에서 문자열 ‘li’를 찾는 awk 프로그램을 실행시킨다(문자를 묶은 것은 보통 문자열(string)이라 부른다. string 이라는 용어는 영어에서 “a string of pearls”나 “a string of cars in a train”같은 유사한 용례에 기반한다):

awk '/li/ { print $0 }' mail-list

이 프로그램이 ‘li’가 포함된 행을 찾으면, 그 행을 출력한다. 왜냐하면 ‘print $0’은 현재 행을 출력하라는 의미이기 때문이다. (‘print’자체만으로도 같은 의미를 가지기 때문에 바꿔 쓸 수 있다.)

awk 프로그램 내에서 문자열 ‘li’를 슬래시(‘/’)으로 둘러 싼 것을 볼 수 있다. 슬래시 ‘li’가 탐색할 패턴이라는 것을 나타낸다. 이런 패턴은 정규 표현식이라 부르며, 차후에 서 상세히 다룰 것이다(정규 표현식 참고). 패턴은 단어의 일부에 대응시킬 수 있다. awk 프로그램을 작은 따옴표가 감싸고 있는데, 그 이유는 셸의 특수 문자로 해석하지 않도록 하기 위해서이다.

다음은 이 프로그램이 출력하는 것이다:

$ awk '/li/ { print $0 }' mail-list
-| Amelia       555-5553     amelia.zodiacusque@gmail.com    F
-| Broderick    555-0542     broderick.aliquotiens@yahoo.com R
-| Julie        555-6699     julie.perscrutabor@skeeve.com   F
-| Samuel       555-3430     samuel.lanceolis@shu.edu        A

awk 규칙에 따르면, 패턴이나 동작 중 하나는 생략할 수 있지만, 둘 다 생략할 수는 없다. 패턴이 생략된 경우, 동작은 모든 입력 행에 수행된다. 동작이 생략된 경우, 기본 동작은 패턴에 맞는 모든 행을 출력하는 것이다.

따라서, 이전 예시에서 동작 (print 문과 중괄호)를 생략해도 결과는 같을 것이다: awk 는 패턴 ‘li’에 맞는 모든 행을 출력한다. 그와 달리, print 문을 생략하고서 중괄호를 남겨 둘 경우에는 아무것도 하지 않는 빈 동작을 수행한다(즉, 어떤 행도 출력되지 않는다).

많은 실용적인 awk 프로그램은 고작해야 한두 줄 정도밖에 안 된다. 다음의 예시들은 유용하면서 시작하기에 좋은 프로그램이다. 이 프로그램 중 일부는 아직 다루지 않은 내용을 포함하고 있다. (프로그램의 해설은 무슨 일이 일어나는 지를 알 수 있게 해주겠지만, awk 전문가가 되려면 웹 페이지의 나머지를 읽어야 한다!) 대부분의 예시들은 데이터 파일의 이름을 data로 사용할 것이다. 이것은 단지 자리를 차지하는 것으로, 프로그램을 사용하고 싶으면, data를 원하는 파일 이름으로 바꾸면 된다. 나중에 주석을 보면, 같은 동작을 awk 프로그램은 여럿 있을 수 있다. 어떤 때는 이 예시들로 돌아와서 아래에 있는 프로그램을 다른 방식으로 짜려고 할 수도 있을 것이다:

문자의 개수가 80자 이상인 모든 행을 출력한다:

awk 'length($0) > 80' data

여기서 유일한 규칙은 동작이 없기 때문에 관계형 표현을 사용한다. 따라서 기본 동작인 기록 출력을 수행한다.

입력 행 중 가장 긴 행의 길이를 출력한다:

awk '{ if (length($0) > max) max = length($0) }
     END { print max }' data

이 코드는 모든 입력을 받고 난 이후에 수행하는 END와 관련되어 있다. 이는 BEGIN과 반대의 의미를 가진다.

데이터의 가장 긴 행의 길이를 출력한다:

expand data | awk '{ if (x < length($0)) x = length($0) }
                   END { print "maximum line length is " x }'

이 예시는 이전 것과는 약간 다르다: 입력은 expand 명령어를 사용해서 탭 문자들을 빈 칸으로 바꿔져서, 실제로 비교되는 너비는 오른쪽에 여백이 있는 열로, 각 행의 입력된 문자들의 개수와는 다르다.

적어도 한 문자가 있는 모든 행을 출력한다:

awk 'NF > 0' data

이는 파일에 있는 빈 행을 삭제하는 쉬운 방법이다(또는, 이전 파일과 비슷하지만 빈 행이 삭제된 새로운 파일을 만든다).

0에서 100까지의 숫자 중 무작위로 7개를 출력한다:

awk 'BEGIN { for (i = 1; i <= 7; i++)
                 print int(101 * rand()) }'

파일에 사용된 전체 바이트 수를 출력한다:

ls -l files | awk '{ x += $5 }
                   END { print "total bytes: " x }'

파일에 사용된 전체 킬로바이트 수를 출력한다:

ls -l files | awk '{ x += $5 }
   END { print "total K-bytes:", x / 1024 }'

모든 사용자들의 로그인한 이름의 정렬된 목록을 출력한다:

awk -F: '{ print $1 }' /etc/passwd | sort

파일의 행의 수를 센다:

awk 'END { print NR }' data

데이터 파일의 짝수 번째 행을 출력한다:

awk 'NR % 2 == 0' data

위의 표현을 ‘NR % 2 == 1’으로 사용하면, 프로그램은 홀수 번째 행을 출력한다.

규칙이 둘인 예시[+/-]

awk 유틸리티는 입력 파일에서 한 번에 한 행을 읽는다. 각 행에 대해서 awk 프로그램은 각 규칙의 패턴에 대입해 본다. 만약 여러 패턴에 부합한다면, 각각의 동작을 awk 프로그램에서 나타나는 순서대로 수행한다. 어떤 패턴에도 부합하지 않는다면, 어떤 동작도 수행하지 않는다.

그 행에 맞는 규칙을 모두 처리하고 나면(없을 수도 있다), awk는 다음 행을 읽는다. (하지만, Next 문Nextfile 문을 보라.) 이는 프로그램이 파일의 끝에 도달할 때 까지 계속된다. 예를 들어, 다음 awk 프로그램은 두개의 규칙으로 되어 있다:

/12/  { print $0 }
/21/  { print $0 }

첫 번째 규칙은 패턴이 문자열 ‘12’이고 동작이 ‘print $0’이다. 두 번째 규칙은 패턴이 문자열 ‘21’이고 동작이 ‘print $0’이다. 각 규칙의 동작은 중괄호 한 쌍으로 싸여 있다.

이 프로그램은 문자열 ‘12’나 문자열 ‘21’을 포함하는 모든 행을 출력한다. 만약 어떤 행이 두 문자열을 동시에 포함하고 있다면, 각각의 규칙에서 한 번씩, 총 두 번 출력된다.

다음은 이 프로그램을 샘플 데이터 메일 목록과 재고 출하량에 대해서 실행할 때 일어나는 것이다:

$ awk '/12/ { print $0 }
>      /21/ { print $0 }' mail-list inventory-shipped
-| Anthony      555-3412     anthony.asserturo@hotmail.com   A
-| Camilla      555-2912     camilla.infusarum@skynet.be     R
-| Fabius       555-1234     fabius.undevicesimus@ucb.edu    F
-| Jean-Paul    555-2127     jeanpaul.campanorum@nyu.edu     R
-| Jean-Paul    555-2127     jeanpaul.campanorum@nyu.edu     R
-| Jan  21  36  64 620
-| Apr  21  70  74 514

메일 목록의 ‘Jean-Paul’로 시작하는 행이 각 규칙에서 한 번씩, 총 두 번 출력된 것을 주목하라.

더 복잡한 예시[+/-]

이제 간단한 작업에 숙달되었다면, 특정한 awk 프로그램이 하는 것을 볼 차례이다. 이 예시는 awk가 어떻게 다른 유틸리티의 출력을 요약하고, 선택하고, 재배열 하는 지를 보여준다. 이 예시는 아직 다루지 않은 기능을 사용하므로, 세부 내용을 이해하지 못 하더라도 걱정할 필요 없다:

ls -l | awk '$6 == "Nov" { sum += $5 }
             END { print sum }'

이 명령어는 현재 디랙토리에 있는 (어던 해든) 11월에 마지막으로 수정된 파일들의 전체 바이트 수를 출력한다. 이 예시에서 ‘ls -l’ 부분은 디렉토리에 있는 파일을 파일의 크기와 마지막으로 수정된 날짜의 목록을 출력하는 시스템 명령어이다. 실행 하면 다음과 같이 출력한다:

-rw-r--r--  1 arnold   user   1933 Nov  7 13:05 Makefile
-rw-r--r--  1 arnold   user  10809 Nov  7 13:03 awk.h
-rw-r--r--  1 arnold   user    983 Apr 13 12:14 awk.tab.h
-rw-r--r--  1 arnold   user  31869 Jun 15 12:20 awkgram.y
-rw-r--r--  1 arnold   user  22414 Nov  7 13:03 awk1.c
-rw-r--r--  1 arnold   user  37455 Nov  7 13:03 awk2.c
-rw-r--r--  1 arnold   user  27511 Dec  9 13:07 awk3.c
-rw-r--r--  1 arnold   user   7989 Nov  7 13:03 awk4.c

첫 번째 영역은 읽고 쓰기 권한을 포함하고, 두 번째 영역은 파일로 연결되는 링크 수를 포함하며, 세 번째 영역은 파일의 소유자를 나타낸다. 네 번째 영역은 파일의 그룹을 나타낸다. 다섯 번째 영역은 파일의 크기를 바이트 수로 나타낸 것이다. 여섯 번째와, 일곱 번째, 그리고 여덟 번째 영역은 각각 마지막으로 수정된 달, 일, 그리고 시간을 나타낸다. 마지막 아홉 번째 영역은 파일의 이름이다.

위의 awk 프로그램에서 ‘$6 == "Nov"’는 ‘ls -l’의 출력의 여섯 번째 영역이 문자열 ‘Nov’인지를 확인하는 표현이다. 어떤 행에서 문자열 ‘Nov’가 여섯 번째 영역에 나타날 때 마다, awk는 동작 ‘sum += $5’을 수행한다. 이는 다섯 번째 영역(파일의 크기)을 변수 sum에 더한다. 따라서, awk가 모든 입력 행을 읽고 나면, sum은 패턴에 맞는 전체 파일 크기가 된다. (이는 awk 변수가 자동으로 0으로 초기화 되기 때문이다.)

ls의 마지막 행이 처리되고 나면, END 규칙이 수행되고 변수 sum의 값을 출력한다. 이 예시에서, 변수 sum의 값은 80600이다.

이 고급 awk 기술은 이후 단락에서 다룰 것이다(동작 개요 참고). 고급 awk 프로그래밍으로 넘어가기 전에, awk가 입력을 어떻게 해석하고 어떻게 출력하는지를 알아야 한다. 영역을 조작하고 print 문을 사용하면, 매우 유용하고 인상적인 결과를 얻을 수 있다.

awk 문과 행[+/-]

보통, awk 프로그램의 각 행은 다음과 같이 문이나 규칙을 분리한다:

awk '/12/  { print $0 }
     /21/  { print $0 }' mail-list inventory-shipped

하지만, gawk는 다음의 어떤 기호나 키워드 뒤의 개행을 무시한다:

,    {    ?    :    ||    &&    do    else

다른 곳에서의 개행은 문의 종료를 의미한다.[2]

만약 하나의 문에서 개행이 있을 시 종료되는 지점에서 단순히 두 줄로 나누고 싶을 때, 첫 줄을 역슬래시 문자(‘\’)로 끝내면 다음 줄에서 계속할 수 있다. 역슬래시가 연속 문자로 인식되기 위해서는 반드시 그 행의 마지막 문자여야 한다. 역슬래시는 문의 어디에서나 사용 가능해서, 심지어는 문자열이나 정규 표현식의 중간에서도 사용할 수 있다. 다음은 예시다:

awk '/This regular expression is too long, so continue it\
 on the next line/ { print $1 }'

이 문서의 예시 프로그램에서는 일반적으로 역슬래시를 연속 문자로 이용하지 않는다. gawk는 한 행의 길이에 제한을 두고 있지 않기 때문에, 역슬래시 연속이 반드시 필요할 상황은 일어나지 않는다, 단지 읽기에 좋게 만들 뿐이다. 같은 이유로, 명료하게 하기 위해서, 웹 페이지 전반적으로 나타낸 프로그램에서 짧은 문을 사용할 것이다. 역슬래시 연속은 awk 프로그램이 커맨드 라인에서 입력되는게 아니라 여러 소스 파일에서 사용될 때 유용하다. 또한 많은 awk 수행은 역슬래시 연속을 할 수 있는 위치가 더 제한적이라는 것에 주목해야 한다. 예를 들어, 역슬래시 연속으로 문자열 상수를 나누는 것을 불가능하다. 따라서, 이제부터 만들 프로그램의 최대 호환성을 위해서 정규 표현식이나 문자열의 중간에서 행을 나누지 않는 것이 최선이다.

경고: 역슬래시 연속은 C 셸에서는 설명한 대로 동작하지 않는다. 이는 유닉스 본 셸이나 배시같은 POSIX-호환 셸을 사용할 경우, awk 프로그램이 파일에 저장되어 있거나 일회성 프로그램에서만 동작한다. 하지만 C 셸은 다르게 동작한다! 한 행에 역슬래시가 두 개 연속으로 있고, 그 다음에 개행이 있어야 한다. C 셸을 사용할 때 awk 프로그램의 모든 개행은 역슬래시로 탈출시켜야 한다. 나타내면 다음과 같다:
% awk 'BEGIN { \
?   print \\
?       "hello, world" \
? }'
-| hello, world

여기서, ‘%’와 ‘?’는 C 셸의 일차 프롬포트와 이차 프롬포트로 표준 셸의 ‘$’와 ‘>’와 유사하다.

이전 예시와 비교해서 위의 내용이 POSIX-호환 셸에서 어떻게 동작하는지를 볼 것이다:

$ awk 'BEGIN {
>   print \
>       "hello, world"
> }'
-| hello, world

awk는 행-기반 언어이다. 각 규칙의 동작은 패턴과 같은 행에 있어야 한다. 패턴과 동작을 다른 행에 두려면, 반드시 역슬래시 연속이 필요하다. 이는 필수적이다.

또다른 기억해야 할 것은 역슬래시 연속과 주석은 혼용해선 안된다. awk가 주석의 시작으로 ‘#’를 보는 순간부터, 나머지 행의 모든 것을 무시한다. 그 예시:

$ gawk 'BEGIN { print "dont panic" # a friendly \
>                                    BEGIN rule
> }'
error→ gawk: cmd. line:2:                BEGIN rule
error→ gawk: cmd. line:2:                ^ syntax error

이 경우에, 역슬래시가 주석 다음 행으로 연속시켜 줄 것으로 보이지만, 역슬래시-개행 조합은 주석에 “숨겨져서” 인식되지도 않는다. 따라서, BEGIN은 구문 오류로 인식한다.

규칙에서 짧은 awk 문이 있을 경우, 여럿을 한 행에 두고 싶을 수 있다. 그럴 때는 세미콜론(‘;’)을 이용해서 여러 문을 한 행에 둘 수 있다. 아는 규칙 자체에도 적용된다. 따라서, 이 문단이 시작할 때 보여준 프로그램은 이렇게 쓸 수 있다:

/12/ { print $0 } ; /21/ { print $0 }

참고: 같은 행에 있는 규칙은 세미콜론으로 분리되어야 한다는 문법은 원래 awk 언어에 있지 않았었다. 이 문법은 동작의 문의 처리와의 일관성을 위해 추가되었다.

awk의 다른 기능[+/-]

awk 언어는 프로그램이 awk에서 정보를 얻을 수 있는 미리 정의된, 또는 빌트인 변수를 제공한다. 또한 awk가 데이터를 어떻게 처리할 지를 지정할 수 있는 다른 변수도 있다.

게다가, awk는 공통 계산이나 문자열 관련 연산을 하기 위한 빌트인 함수를 제공한다. gawk는 타임스탬프나, 비트 단위 조작, 런타임 문자열 변환 (전역변수화), 변수의 타입 지정, 그리고 배열 정렬을 하는 빌트인 함수를 제공한다.

우리가 awk 언어의 프레젠테이션을 만들었기 때문에, 우리는 변수와 함수의 대부분을 소개할 것이다. 이 변수와 함수는 시스템 적으로는 빌트인 변수(Built-in Variables)와 빌트인(Built-in)으로 부른다.

awk를 사용할 시점[+/-]

이제 awk가 무엇을 할 수 있는지를 약간 알았으니, awk가 어떤 점에서 유용할 지 궁금할 수 있다. 유틸리티 프로그램과, 필드 분리자, 산술 문, 그리고 다른 선택 조건을 사용하면, 더 복잡한 결과를 만들 수 있다. awk 언어는 ls 같은 다른 유틸리티 프로그램의 출력에서 요약하는 것 같이 많은 양의 원시 데이터에서 리포트를 만들 때 유용하다. (더 복잡한 예시 참고.)

awk로 쓰여진 프로그램은 다른 언어로 쓰여진 것보다 훨씬 짧다. 이런 점이 awk 프로그램을 만들고 쓰기 쉽게 만든다. awk 프로그램은 종종 키보드로 빨리 만들고, 한번 쓰고, 지울 수 있다. 왜냐하면 awk 프로그램이 해석될 때, 소프트웨어 개발에서 특정 편집-컴파일-테스트-디버그의 사이클의 (보통은 긴) 컴파일 부분을 피할 수 있기 때문이다.

8비트 마이크로프로세서(더 자세한 정보를 위해서는 사전 참고)를 위한 재 타겟팅 가능한 완전한 어셈블러나, 특수목적 프롤로그 컴퓨터를 위한 마이크로코드 어셈블러를 포함하는 복잡한 프로그램은 awk로 쓰여졌다. 원래 awk의 능력은 이런 완전성을 위한 업무에 제약을 가지지만, 최신 버전은 제약이 더 적다.

만약 awk 프로그램을 한 몇 천 줄 이상을 적고 있다면, 아마 다른 프로그래밍 언어를 사용하는 것을 고려해야 한다. 이 셸은 문자역과 패턴 비교에 적합하다. 게다가, 시스템 유틸리티의 효과적인 사용을 가능하게 한다. 파이썬은 높은 수준의 프로그래밍 용이성과 시스템 시설 접근간에 균형 잡혀있다.[3]

요약[+/-]

  • awk의 프로그램은 패턴-동작 쌍으로 이루어져 있다.
  • 패턴이 없는 동작은 항상 수행된다. 패턴에 동작이 없을 때 수행되는 기본 동작은 ‘{ print $0 }’이다.
  • awk를 실행 시키려면 ‘awk 'program' files’나 ‘awk -f program-file files’을 사용해야 한다.
  • 특수한 ‘#!’헤더 라인을 사용해서 awk 프로그램을 바로 실행 가능하게 할 수 있다.
  • awk의 주석은 ‘#’에서 시작해서 그 행이 끝날 때 까지 계속된다.
  • awk 프로그램을 큰 셸 스크립트(나 MS 윈도우 배치 파일)의 일부로 사용할 때, 따옴표를 기억하라.
  • 역슬래시를 사용해서 소스 행에서 연속시킬 수 있다. 행은 콤마나, 여는 중괄호, 물음표, 콜론, ‘||’, ‘&&’, do, 그리고 else에서 자동으로 연결된다.
  1. ‘#!’ 메커니즘은 GNU/리눅스 시스템, BSD 기반 시스템, 그리고 상업용 유닉스 시스템에서 작동한다.
  2. 여기서 나오는‘?’와 ‘:’는 조건 표현에 나오는 삼항 조건 연산자를 가리킨다. ‘?’와 ‘:’ 의 뒤에서 행을 나누는 것은 사소한 gawk 확장이다. 만약 --posix가 지정되었으면(설정 참고), 이 확장은 비활성화 된다.
  3. 다른 유명한 스크립트 언어에는 Ruby와 Perl을 포함한다.