변환 처리_08 HTML 파일에서 태그 속에 적힌 주석을 추출해서 그대로 실행하기

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

명령어: sed, eval
키워드: 명령어, 변수 확장
사용처: 파일에 적힌 문자열을 추출해서 명령어로 실행하고 싶을 때


실행예제

$ ./eval.sh
Sat Jul 4 21:41:24 JST 2015
-rw-rw-r--. 1 user1 user1 11968 Oct 26 12:32 myapp.log

스크립트

#!/bin/sh

filename="myapp.log"
eval $(sed -n "s/<code>\(.*\)<\/code>/\1/p" command.htm)

   

해설

이 스크립트는 command.htm 파일 안에 적힌 태그를 추출해서 그 요소를 명령어로 실행합니다.   command.htm 파일 내용은 [파일1]과 같습니다. 즉, "date; ls -l $filename"을 명령어로 실행한다고 가정합니다. 이때 셸 스크립트에서 $filename 변수도 확장합니다.

  • 파일1 처리 대상 HTML 파일 예제(command.htm)

    <html>
    <head><title>Code List</title></head>
    
    <body>
    <p>This is a sample code.</p>
    <code>date; ls -l $filename</code>
    
    </body>
    </html>
    

스크립트에서는 우선 태그 부분을 패턴 매치해서 추출합니다. 셸 스크립트에서 일치한 부분의 문자열을 추출하는 방법은 다양하지만 여기에서는 다음처럼 **sed 명령어**를 사용합니다.

sed -n "s/<code>\(.*\)<\/code>/\p" command.htm

sed 명령어의 -n 옵션은 처리 후에 패턴 스페이스 내용을 출력하지 않도록 하는 옵션입니다. 그대로는 아무것도 출력되지 않아서 의미가 없으므로 마지막에 p 플래그를 붙여서 일치했을 때만 패턴 스페이스를 출력하도록 지정합니다.

이런 -n 옵션과 p 플래그를 조합해서 sed 명령어로 치환하면 치환이 발생한 줄만 출력할 수 있습니다. sed 명령어에서 자주 쓰는 방식이므로 기억해두기 바랍니다.

그럼 이번에는 일치한 줄 중 태그 부분만 추출하고 싶으므로 **후방참조 \1**을 사용합니다. sed 명령어의 일치 확인에서 ()로 둘러싼 부분은 앞에서부터 순서대로 \1, \2, ...으로 참조할 수 있습니다. 여기에서 안에 임의의 문자열 .*를 ()로 둘러싸서 \1로 추출합니다. \1 부분이 실행하고 싶은 명령어 문자열이 됩니다.

그럼 sed 정규표현식은 후방참조하는 부분의 괄호를 ()가 아니라 ()를 사용하므로 이 예제에서도 ()를 씁니다.

이걸로 명령어 문자열을 추출했으므로 eval로 변수 확장을 해서 명령어를 실행합니다. eval은 인수로 넘어온 문자열을 변수 확장해서 명령어로 실행합니다. 여기서 sed 명령어 출력은 다음과 같은 문자열입니다.

date; ls -l $filename

eval 명령어에 이 문자열을 인수로 넘기면 셸 변수 filename이 치환되어 결과로 이런 코드가 실행됩니다.

date; ls -l myapp.log

이렇게 eval 명령어를 사용하면 셸 스크립트 코드 자체를 동적으로 생성해서 실행할 수 있습니다.

한편 eval 명령어는 사용법에 따라서는 메타프로그래밍처럼 쓸 수도 있어 편리합니다. 하지만 텍스트 문자열을 그냥 명령어로 실행하므로 사용법에 따라서는 악의적인 코드가 실행될 수 있으니 주의해야 합니다.

예를 들어 사용자로부터 입력받은 문자열에는 공격 코드가 들어 있을 수도 있습니다. 이렇듯 시스템 외부에서 입력받은 값을 부주의하게 eval로 실행하는 행위는 절대로 하면 안됩니다. OS 명령어 인젝션 같은 보안 문제가 발생하게 됩니다.

   

주의사항

  • 이 예제에서 sed로 태그를 처리하므로 다음과 같이 태그 안에 줄바꿈이 있으면 제대로 동작하지 않습니다.

    <code>
      date; ls -l $filename
    </code>
    
  • eval 명령어는 인수 문자열을 명령어로 해석해서 그대로 실행해버리므로 입력값에 주의해야 합니다. 예를 들어 만약 다음과 같은 문자열이 태그에 있다고 합시다.

    rm -rf ~/*
    

    이 스크립트를 실행하면 ~가 홈 디렉터리로 확장되어 실행한 사람의 홈 디렉터리 내부를 전부 삭제해버립니다. eval로 실행하는 스크립트를 만들 때에는 우선 eval 부분을 echo로 바꿔서 실행할 때 실제로 어떤 명령어 내용이 나오는지 확인해봅시다. 또한 사용자 입력값처럼 뭐가 들어 있을지 모르는 문자열을 eval로 실행하는 행위는 위험합니다.