기초

기초

학습 연습 문제

소개

기본 개념

여기서는 _언어_를 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의 차이를 보여 주는, 흔히 쓰이는 AWK 관용구가 있어요.
      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 파일을 관리하며, 파일을 최신 상태로 안전하게 유지하고, 권한이 있는 사용자만 시스템에 접근할 수 있도록 했어요.

이제 알렉스처럼 시스템 관리자가 될 차례예요.

Note

이 연습 문제를 풀기 위해 필요한 추가 AWK 문법이에요.

  • 불리언 연산자는 다른 언어와 비슷해요:

    연산 예시
    AND expr1 && expr2
    OR expr1 || expr2
    NOT ! expr
  • 함수를 호출하는 문법도 익숙할 거예요:

    function greet(name) {
        print "Hello", name
    }
    BEGIN {
        greet("Mary")   # prints "Hello Mary"
    }
    

1. 사용자 이름 나열하기

passwd 파일의 각 줄에서 콜론으로 구분된 첫 번째 필드를 출력하는 awk 프로그램을 작성해 보세요.

2. 줄 번호 출력하기

입력 파일의 각 레코드에 대한 줄 번호를 출력하는 awk 명령을 작성해 보세요. 줄 번호만 출력하고, 다른 정보는 출력하지 마세요.

3. 유효하지 않은 홈 디렉터리를 가진 레코드 출력하기

사이버스페이스에서 "유효한" 홈 디렉터리는 /home 또는 /root로 시작해요. 홈 디렉터리가 유효하지 않은 입력 파일의 레코드를 출력해 보세요.

4. bash를 사용하는 유효한 사용자 출력하기

홈 디렉터리가 유효하고 동시에 로그인 셸이 bash인 레코드를 출력해 보세요.


출처

ChatGPT와의 대화
GitHub에서 편집 링크가 새 창이나 탭에서 열려요
AWK Exercism

기초 문제를 시작해 볼 준비가 됐나요?

Exercism에 가입하고 AWK 트랙을 개념 3개연습 문제 92개, 그리고 실제 사람의 멘토링과 함께 배우고 익혀 보세요. 모두 무료예요.