텍스트처리_09 로그 파일 컬럼 위치를 바꿔서 출력하고 보기 쉽게 바꾸기

 
  • 출처 : 유닉스 리눅스 쉘스크립트 예제사전_한빛미디어

명령어: awk
키워드: 접속 로그, 로그 해석, 변형
사용처: 아파치 접속 로그에서 필요한 컬럼을 추출하고, 순서를 바꾸고 싶을 때


실행예제

$ cat access_log
xx.xx.xx.xx - - [06/Jan/2014:05:58:35 +0900] "GET / HTTP/1.1" 200 83 "-" "-"
yy.yy.yy.yy - - [06/Jan/2014:06:01:43 +0900] "GET /index.html HTTP/1.1" 200 304
yy.yy.yy.yy - - [06/Jan/2014:06:01:44 +0900] "GET /title.gif HTTP/1.1" 200 763

$ ./log-column.sh access.log

# 로그 파일에서 시각과 원격 호스트를 추출해서 순서를 바꿔서 출력
$ cat access_log.lst
[06/Jan/2014:05:58:35 +0900] xx.xx.xx.xx
[06/Jan/2014:06:01:43 +0900] yy.yy.yy.yy
[06/Jan/2014:06:01:44 +0900] yy.yy.yy.yy

스크립트

#!/bin/sh

# 로그 파일이 존재하지 않으면 종료
if [ ! -f "$1" ]; then  #----------------------------- 1(if문)
    echo "대상 로그파일이 존재하지 않습니다: $1" >&2
    exit 1
fi

# 요청 시각과 원격 호스트를 외부 파일에 출력
awk '{print $4,$5,$1}' "$1" > "${1}.lst"  #----------- 2

   

해설

이 스크립트는 아파치 접속 로그에서 필요한 열을 추출해서 순서를 바꿔서 출력합니다. 로그 해석은 같은 조건으로 대량의 파일에서 추출할 때가 많으므로 이런 스크립트로 일괄 처리할 수 있습니다.

아파치는 접속 로그를 원하는대로 변경할 수 있으므로 실행예제에서 다루는 로그 형식과 실제 환경에서 작성된 로그는 조금씩 다를 수 잇습니다. 여기에서 사용하는 접속 로그는 일반적으로 사용하는 common이라는 설정을 사용합니다.

  • 아파치 common 형식 로그 예
    192.168.1.1 - - [06/Jan/2014:05:58:35 +0900] "GET /index.html HTTP/1.1" 200 83
    

로그의 항목별 의미는 다음과 같습니다.

  • 아파치 로그 읽는 방법

    의미 예제
    원격호스트 192.168.1.1
    identd의 원격 사용자명 -
    인증의 원격 사용자명 -
    리퀘스트 받은 시각 [06/Jan/2014:05:58:35 +0900]
    리퀘스트 첫 줄 “GET /index.html HTTP/1.1”
    HTTP 스테이터스 200
    레스폰스 바이트 수 83

‘identd 원격 사용자명’은 mod_ident라는 아파치 모듈에서 제공합니다. 아파치에서 idnetd로 사용자명을 취득하는 경우는 거의 없으므로 대부분 “-“이 출력됩니다.

‘인증 원격 사용자명’은 BASIC 인증 등에서 입력된 사용자명입니다. 인증이 없는 페이지는 단순히 “-“이 됩니다.

‘리퀘스트 첫  줄’은 “HTTP메서드명 리퀘스트URI(파일명) HTTP버전”이 됩니다. 브라우저에서 웹 페이지를 볼 때 이런 리퀘스트 명령행을 직접 볼 일은 거의 없지만 내부적으로 웹 브라우저 웹 서버에 대해 이런 리퀘스트를 발행해서 웹 페이지를 취득합니다.

예제에서는 이런 로그를 바탕으로 파일명이 (로그 파일명).lst인 파일에 첫 번째 컬럼은 리퀘스트 시각, 두 번째 컬럼은 원격 호스트로 출력된다고 가정합니다.

우선 스크립트에서 인수로 로그 파일명을 받으므로 1에서는 명령행 인수를 확인하고 파일 존재 여부를 알아봅니다. -f는 일반 파일인지 확인하는 연산자로 부정 연산자 !와 함께 써서, 일반 파일이 아닐 때 에러를 출력하고 종료합니다.

2는 로그 파일에서 awk 명령어로 필요한 열을 추출합니다. awk 명령어는 액션이라 부르는 중괄호 { } 로 둘러싼 부분에서 다양한 출력이 가능합니다. “$4,$5,$1”이란 awk 명령어 내부 변수로 각각 네번째, 다섯 번재, 첫 번째 컬럼을 의미합니다.

awk 명령에서 공백문자는 기본 구분자이므로 로그 예에서 나오는 리퀘스트 시각[06/Jan/2014:05:58:35 +0900]는 시차를 표시하는 “+0900” 앞에 스페이스가 있어서 awk에서 다른 컬럼으로 인식해버립니다. 따라서 네 번째와 다섯 번째 컬럼을 “$4,$5”로 나란히 print 합니다.그리고 awk 명령어에서 print 할 때는 쉼표가 스페이스가 됩니다. 이때 스페이스 기호로 print할 변수를 나열하면 스페이스를 무시하기 때문에 주의해야 합니다.

$ awk '{print $4 $5 $1}' access_log
[06/Jan/2014:05:58:35 +0900]xx.xx.xx.xx
[06/Jan/2014:06:01:43 +0900]yy.yy.yy.yy
[06/Jan/2014:06:01:44 +0900]yy.yy.yy.yy

이렇게 awk 명령어로 표시하고 싶은 컬럼 위치의 순서를 원하는 대로 바꿀 수있으므로 로그 파일을 변형할 수 있습니다.   2에서 사용하는 $1 기호는 awk 변수와 셸 스크립트 위치 파라미터 변수 두 개가 등장합니다. 서로 완전히 다르므로 주의하기 바랍니다. awk의 print문 안에 있는 $1은 awk 내장 변수로 ‘첫 번째 컬럼값’을 의미합니다. 한편, 셸 스크립트의$1은 ‘첫 번째 명령행 인수’가 됩니다.   상황에 따라서는 파일명을 다른 변수에 대입해서 작성하는 것이 좋을 때도 있습니다.

logfile="$1"
awk '{print $4,$5,$1}' "$logfile" > "${logfile}.lst"