여기서는 _언어_를 AWK라고 부르고, AWK 프로그램을 해석하는 _실행 파일_은 awk 또는 **gawk**라고 불러요.
AWK 프로그램은 패턴-액션 쌍으로 구성돼요.
pattern1 { action1 }
pattern2 { action2 }
...
패턴은 입력과 일치시키는 정규 표현식 패턴이 될 수 있어요. 참/거짓 값을 만들어 내는 어떤 표현식이든 될 수도 있어요.
액션은 하나 이상의 AWK 명령이에요. 감싸는 중괄호는 반드시 필요해요.
패턴의 결과가 _참_이면, 그에 해당하는 액션이 실행돼요.
AWK는 입력을 일련의 레코드로 파싱해요. 각 레코드는 0개 이상의 필드로 나뉘어요.
기본 동작은 다음과 같아요:
패턴은 생략할 수 있어요. 그러면 액션이 _모든 입력 레코드_에 대해 실행돼요.
액션도 생략할 수 있는데, 이때 기본 동작은 현재 레코드를 출력하는 거예요.
자주 사용하는 특별한 패턴이 두 가지 있어요.
BEGIN은 입력을 읽기 전에 연결된 액션을 실행해요.END는 모든 입력을 다 읽은 뒤에 연결된 액션을 실행해요.필드는 $1, $2, $3 등으로 가리켜요.
현재 레코드는 $0이에요.
그 밖에 자주 쓰이는 내장 변수도 있어요.
RS는 입력 레코드 구분자예요.
앞서 말했듯이 기본값은 줄바꿈이에요.FS는 입력 필드 구분자예요.이 변수들은 보통 BEGIN 액션 블록에서 설정해요.
이 변수들은 프로그램이 실행되는 동안 AWK가 자동으로 설정해요.
NF는 현재 레코드의 필드 개수를 저장해요.
$NF를 사용해요.NR은 현재 레코드 번호를 저장해요.
NR은 지금까지 처리한 전체 레코드 개수를 담아요.FNR은 현재 입력 파일에서의 현재 레코드 번호를 저장해요.
NR == FNR {action}
이는 "누적된 레코드 번호가 현재 파일의 레코드 번호와 같다면"이라는 뜻이에요.
이 조건은 _첫 번째 입력 파일_의 레코드에서만 참이 될 수 있어요.AWK는 C와 같은 참/거짓 개념을 사용해요.
0은 거짓이에요.AWK는 텍스트 처리 언어라서 참/거짓 값이 문자열에도 적용돼요.
""은 거짓이에요.주석은 # 문자로 시작해서 줄 끝까지 이어져요.
유닉스 계열 시스템에서 /etc/passwd에는 사용자 정보가 들어 있어요.
각 사용자는 별도의 줄에 설명되는데, 필드는 콜론으로 구분돼요.
사용자 이름을 나열하려면:
awk '
BEGIN { FS = ":" }
{ print $1 }
' /etc/passwd
{print $1} 액션에는 패턴이 없어서, 모든 레코드에 대해 실행된다는 점을 눈여겨봐요.
출력을 로그인 셸로 bash를 사용하는 사용자로만 제한하고 싶다면 이렇게 하면 돼요:
awk '
BEGIN { FS = ":" }
$7 == "/bin/bash" { print $1 }
' /etc/passwd
옛날 옛적에 사이버스페이스라는 활기찬 도시가 있었어요. 사이버스페이스는 디지털 대도시였고, 매일 수백만 명의 주민이 로그인하고 로그아웃했어요. 이 모든 사용자를 관리하기 위해 사이버스페이스에는 /etc/passwd라는 특별한 파일이 있었어요.
/etc/passwd 파일은 이 도시의 전화번호부와 같았어요. 시스템에 계정을 가진 모든 사용자와, 각 사용자에 대한 몇 가지 중요한 정보가 나열되어 있었어요:
| n | 필드 |
|---|---|
| 1 | 사용자 이름 |
| 2 | 비밀번호 ("x"는 보안을 위해 실제 비밀번호가 다른 곳에 저장되어 있음을 나타내요) |
| 3 | UID, 고유한 숫자 사용자 ID |
| 4 | GID, 고유한 숫자 그룹 ID (일반적으로 UID와 같아요) |
| 5 | 사용자 정보 (일반적으로 사용자의 전체 이름이지만, 다른 정보도 포함될 수 있어요) |
| 6 | 홈 디렉터리 |
| 7 | 로그인 셸 |
각 필드는 콜론 문자로 구분되어 있어요. 다음은 전형적인 항목이에요:
glennj:x:1010:1010:Glenn Jackman:/home/glennj:/usr/local/bin/fish
이 파일은 시스템 관리자 팀이 관리했는데, 이들은 새 계정을 만들고, 기존 계정을 갱신하고, 오래된 계정을 삭제하는 일을 맡았어요. 시스템 관리자는 사이버스페이스의 문지기로서, 권한이 있는 사용자만 시스템에 접근할 수 있도록 했어요.
어느 날, 알렉스라는 새 시스템 관리자가 팀에 합류하게 되었어요. 알렉스는 새 일을 맡게 되어 신이 났고, /etc/passwd 파일의 이모저모를 금방 익혔어요. 시스템에 새 사용자를 추가하는 방법, 사용자 정보를 갱신하는 방법, 오래된 사용자를 삭제하는 방법을 배웠어요.
알렉스가 /etc/passwd 파일을 살펴보던 중, 뭔가 이상한 점을 발견했어요. 일부 사용자의 홈 디렉터리는 이상하거나 특이했어요. 어떤 사용자의 홈 디렉터리는 /dev/null로 설정되어 있었고, 또 어떤 사용자의 홈 디렉터리는 존재하지 않는 낯선 경로로 설정되어 있었어요.
알렉스는 이 사용자들이 실제 사람이 아니라 해커가 만들어 둔 가짜 계정이라는 것을 깨달았어요. 해커들은 시스템의 취약점을 악용할 방법을 찾아내서, 사이버스페이스에 접근하려고 이 가짜 계정들을 만들어 둔 것이었어요.
알렉스는 조치를 취해야 한다는 것을 알았어요. 알렉스는 awk를 사용해 /etc/passwd 파일을 분석하고, 모든 가짜 계정을 찾아내는 스크립트를 작성했어요. 그다음 팀원들과 함께 이 계정들을 삭제하고, 애초에 해커들이 이런 계정을 만들 수 있게 했던 취약점을 고쳤어요.
알렉스가 awk를 활용한 덕분에, 팀은 가짜 계정을 찾아내고 삭제해서 사이버스페이스를 모두에게 더 안전한 곳으로 만들 수 있었어요. 시스템 관리자들은 계속해서 /etc/passwd 파일을 관리하며, 파일을 최신 상태로 안전하게 유지하고, 권한이 있는 사용자만 시스템에 접근할 수 있도록 했어요.
이제 알렉스처럼 시스템 관리자가 될 차례예요.
이 연습 문제를 풀기 위해 필요한 추가 AWK 문법이에요.
불리언 연산자는 다른 언어와 비슷해요:
| 연산 | 예시 |
|---|---|
| AND | expr1 && expr2 |
| OR | expr1 || expr2 |
| NOT | ! expr |
함수를 호출하는 문법도 익숙할 거예요:
function greet(name) {
print "Hello", name
}
BEGIN {
greet("Mary") # prints "Hello Mary"
}
passwd 파일의 각 줄에서 콜론으로 구분된 첫 번째 필드를 출력하는 awk 프로그램을 작성해 보세요.
입력 파일의 각 레코드에 대한 줄 번호를 출력하는 awk 명령을 작성해 보세요. 줄 번호만 출력하고, 다른 정보는 출력하지 마세요.
사이버스페이스에서 "유효한" 홈 디렉터리는 /home 또는 /root로 시작해요.
홈 디렉터리가 유효하지 않은 입력 파일의 레코드를 출력해 보세요.
홈 디렉터리가 유효하고 동시에 로그인 셸이 bash인 레코드를 출력해 보세요.