2016년 7월 14일 목요일

VIM 사용 설명서 - 3rd

지난 시간에 살펴봤던 문자열 치환 기능은 당연히 문자열 단위로만 동작한다. 원본 데이터의 줄 단위 구조는 바뀌지 않는다는 얘기. 그런데 가끔 줄 단위 구조를 바꾸고 싶을 때가 있다. 예를 들면 특정 문자열을 포함한 줄만을 골라서 삭제한다든지.

글로벌 명령어

그런 기능이 있을까? 이런 기능이 있으면 좋겠다 싶어서 구글링 해보면 VIM은 신기하게도 반드시 그 기능을 가지고 있더라.


해당 기능을 어디에 써먹으면 좋을까? 10만 줄 가량의 웹로그가 하나 있다. 웹 요청 메써드 사용 현황을 파악해보자. 제대로 하려면
  1. 관련 데이터를 영역별로 추출한 후, 
  2. 영역별 필드 구조를 갖는 테이블로 만든 다음,
  3. 통계 분석을 해야 한다.


그런데 시간은 없고(귀찮기도 하고), 대략적인 현황을 빠르게 파악해야 하는 상황이다. 그럴 때 해당 기능이 딱이다.

다음은 명령어 ':g/get \//d'를 이용해서, 'get /' 문자열을 포함한 줄을 찾아서 삭제한 결과. 약 14,000개 정도의 줄이 남았다. 사용된 메소드 중 GET이 95% 정도임을 알 수 있다.
(참고로 선 검색, 후 명령 모드 과정을 거친다면 얘도 명령어 실행 시 '검색어 영역'을 비워두는 게 가능)


역으로 동작하게 할 수도 있다. 'get /' 문자열을 포함한 줄을 제외한 나머지를 삭제해보자. g 옵션을 v로 바꾸면 된다.



참고로 부정 옵션 !를 사용(:g!/get \//d)해도 ':v/get \//d'와 같은 결과를 가져오지만 그냥 v 옵션 사용하는 게 더 편하지 않나 싶다. 왠지 더 직관적?


전체가 아닌 특정 줄에 대해서만 명령어를 실행하고 싶다면 문자열 치환 기능처럼 줄 번호를 붙이면 된다. '1g/검색어/d' 또는 '1,100g/검색어/d' 형식.

그런데 삭제 기능만 있을까? 치환도 할 수 있다. 문자열 검색으로 해당 줄 전체의 특징을 찾기 어려울 때 강추. 더 자세한 내용은 :help global.

문자열 데이터의 줄 단위 분포 확인하는 거 말고 또 어디에 써먹을 수 있을까? 특정 로그를 대상으로 검색에 사용한 정규표현식이 정확한지, 전체 줄 단위로 적용이 되는지 궁금할 때가 있다.

사용된 명령어 'g/\v(get|post).*/d'는 GET 또는 POST로 시작하는 모든 문자열을 찾아서 해당 문자열이 포함된 줄을 삭제하라는 의미.


사용했던 정규표현식 검색어는 전체 줄에 적용되지 않으며, 전체 줄에 적용하려면 검색어를 '\v(get|post|options|propfind|head).*' 형식으로 바꿔야 한다는 사실을 알 수 있다. 덤으로 전체 메소드 발생 종류까지 확인.

정렬 기능

전체 메소드 발생 종류를 더 쉽게(?) 확인할 수도 있다. 다음 결과를 보면 첫 번째 그림과 달리 IP 순서가 바뀌었다는 사실을 알 수 있다. 정렬(sort) 기능을 이용한 것.


해당 기능은 줄 단위로 동작하며 시작 문자부터 검사하기 때문에 IP 주소 순으로 정렬이 됐음을 알 수 있다. 메소드별 정렬을 시도해보자. 

다음처럼 특정 문자열을 기준으로 정렬을 시도할 수 있다. 그런데 그다지 정확하지 않다. GET 다음에는 HEAD가 나와야 하는데 -_-


메소드 문자열을 제외한 나머지 문자열을 모두 삭제한 후 정렬을 시도해보자. 해당 웹로그는 URI 정보를 "GET /path/file.html HTTP/1.0" 형식으로 표시하고 있다.

즉 메소드 문자열은 "로 시작한다. 다음은 각 줄에서 첫 번째 문자부터 "까지의 문자열을 검색한 결과.


수량자(*)의 최대한 검색하려는 성질때문에 "가 더 이상 검색이 안될 때까지, 즉 마지막 "까지 검색하고 있다. 검색 범위를 최소로 제한해보자.


수량자 '{-}'를 이용해서 검색 범위를 최소로 제한했더니 첫 번째 "가 나오자마자 검사가 중지되면서 메소드 시작 전까지의 문자열을 검색할 수 있었다. 검색이 되면 치환도 할 수 있다. 지우자.


사용된 치환 명령어는 ':%s///'. 지난 시간에 소개한대로 문자열 치환 형식의 ② 영역은 VIM이 최근 검색어를 자동으로 입력해주기 때문에, ③ 영역은 삭제가 목적이기 때문에 비워놨다.

이제 메소드 이후의 문자열을 모두 지우면 된다. 사용된 검색어는 '공백으로 시작하는 모든 문자'를 의미하는 ' .*'이며, 검색된 결과를 지우는 과정은 이전과 같다.



이제 정렬만 해주면 된다. 알파벳 순으로 정렬이 잘 됐음을 알 수 있으며, 역순으로 정렬하고 싶다면 부정 옵션(!)을 사용하면 된다.



그런데 개체수가 너무 많아서 메소드별 종류 확인은 힘들다. 중복을 제거하면 가능한데(..) 될까? 당근 된다. 'u(nique)' 옵션 등장. 개발자에게 진심 노벨상 주고 싶다.


참고로 sort 명령은 정렬 대상을 문자로 인식하기 때문에 숫자도 문자처럼 정렬한다. 그럴 땐 당황하지 말고 'n(umeric)' 옵션을 사용하자.



VIM은 문자열 데이터를 마음껏 씹고 뜯고 맛보게 해주는 맥가이버칼이다. 기능이 너무 많아서 모르는 기능이 더 많은 맥가이버칼(..)


3회에 걸쳐서 문자열 데이터 분석 시 요긴하게 쓸 수 있는 VIM 기능에 대해서 살펴봤다. 당신은 이제 VIM 전문가 다음 시간에는 VIM의 다양한 기능들을 이용해서 secure 로그 분석 과정을 진행해볼까 한다.

관련 글

댓글 없음:

댓글 쓰기

크리에이티브 커먼즈 라이선스