본문 바로가기
모의해킹 침해대응 과정/본 과정

쉘 스크립트(bash)_2 / day29

by 알거음슴 2021. 5. 4.

1. 조건문 

 1) case 구문

#!/bin/bash
cat << EOF
-----------------------------------------------------------------
    (1) who    (2) date    (3)pwd
-----------------------------------------------------------------
EOF

echo -n "Enter your choice? : "
read NUM1

case $NUM1 in
    1) who ;;
    2) date ;;
    3) pwd ;;
    *) echo "Error"
        exit 1 ;;
esac

 case구문의 경우 case VAR in ~ esac 로 구성되어 있으며, VAR은 변수이다, in ~ esac 사이엔 숫자로 넘버링이 가능하며, 넘버링 끝에는 ;; 로 끝을 알리며, 나머지선택지의 대한 값은 *)로 표현이 된다.

 

 1-1) yes or no

#!/bin/bash

echo -n " Enter Your Choice? (yes/no) : "
read ANSWER

case $ANSWER in
    y|Y|yes|Yes|YES) echo "YES" ;;
    n|N|no|No|NO) echo "NO" ;;
    *) echo " Don't understand the answer ! "
        exit 1 ;;
esac

 위 경우처럼 넘버링이 아닌 입력받을 조건을 여러가지로 둘 수 있다, 이때 또는을 뜻하는 구분자는 | (파이프) 기호이며 and의 의미를 갖는다.

 

 1-2) service를 키고 끄자

예제1)
#!/bin/bash
USAGE() {
    echo " Usage : $0 <service> {start|stop|restart|status}"
}
SERVICENAME=$1
ACTION=$2

case $ACTION in
    start)   systemctl start $SERVICENAME
             systemctl enable $SERVICENAME ;;
    stop)    systemctl stop $SERVICENAME 
             systemctl disable $SERVICENAME ;;
    restart) systemctl restart $SERVICENAME ;;
    status)  systemctl status $SERVICENAME ;;
    *) USAGE
    exit 1 ;;
esac

 서비스 데몬을 키고 끄는 양식이다, 이처럼 명령문도 적용 가능하고, 함수값으로도 가능하다, 특수변수들과 함수를 활용하면 더욱 효율적인 코드를 만들 수 있다. 

 case 구문 내 if 구문도 섞어서 활용 할 수 있다.

#!/bin/bash

USAGE() {
    echo " Usage : $0 <service> {start|stop|restart|status}"
}

if [ $# -ne 2 ] ; then
    USAGE
    exit 1
fi

SERVICENAME=$1
ACTION=$2
STATUS=$(systemctl is-active $SERVICENAME)

case $ACTION in
    start)   systemctl start $SERVICENAME >/dev/null 2>&1
             systemctl enable $SERVICENAME >/dev/null 2>&1
                if [ $STATUS = 'active' ] ; then
                    echo "[   OK   ] Starting $SERVICENAME"
                else
                    echo "[  FAIL  ] Starting $SERVICENAME"
                fi 
            ;;
    stop)    systemctl stop $SERVICENAME >/dev/null 2>&1
             systemctl disable $SERVICENAME >/dev/null 2>&1
                if [ $STATUS = 'inactive' ] ; then
                    echo "[   OK   ] Stoping $SERVICEANME"
                else
                    echo "[  FAIL  ] Stoping $SERVICEANME"
                fi
            ;;
    restart) systemctl restart $SERVICENAME ;;
    status)  systemctl status $SERVICENAME ;;
    *) USAGE
    exit 1 ;;
esac

이처럼 if 구문을 섞어서도 동작이 가능하다, 위의경우에 정상 동작시 [  OK  ] 출력되며, 비정상동작시 [  FAIL  ] 로 출력이 된다

 service.sh 수정.

 

 

2. 반복구문

반복구문은 여러가지 리스트를 반복시켜서 실행시켜주는 동작을 의미한다.

 1) for 구문 ( 단순반복 )

for 구문은 for VAR in VAR_list do ~ done 으로 이루어져 있다. VAR_list 내에 있는값을 VAR 내에 한번씩 넣어서 do ~ done 사이의 내용을 실행하는 방식이다. 예제를 보면 이해가 빠르다

#!/bin/bash

for VAR in 1 2 3 4
do
    echo $VAR
done

[root@linux200 ~/bin]# for.sh
1
2
3
4

1 이 VAR에 한번 들어가서 출력되고 다음으로 2 ,3, 4 순차적으로 리스트가 모두 출력완료되면 종료되는형식이다.

 1-1) 파일안의 내용을 리스트화 하자.

#!/bin/bash

FTPUSERS=/etc/vsftpd/ftpusers

for NAME in $(cat $FTPUSERS | egrep -v '^$|^#')
do
    echo $NAME
done

/etc/vsftpd/ftpusers 에는 ftp 사용가능 유저들의 목록이 있다. 중간에 주석처리(#) 또는 공백을 제외하고 유저의 리스트를 뽑아서 변수리스트로 지정할 수 도 있다, 리스트를 일일이 나열하는것이 아닌 파일을 활용하여 적용할때 cat 과 grep를 활용할 수 있다.

 

 2) while 구문 ( 무한반복 )

while 구문의 경우 while (참) do ~ done 으로 구성되어있다. 참일경우 do ~ done 을 실행하고 거짓이 이 되면 종료한다, 즉 참이 계속 반복되는 값이 있을 경우 거짓이 될때 까지 반복하여 적용되는 구문이다. 예시를 보자

#!/bin/bash

while [ $# -gt 0 ]
do
    echo "$# : $*"
    shift
done

[root@linux200 ~/bin]# shift.sh 1 2 3 4 5 6
6 : 1 2 3 4 5 6
5 : 2 3 4 5 6
4 : 3 4 5 6
3 : 4 5 6
2 : 5 6
1 : 6

 예시 처럼 echo "$# : $*"  -> shift 의 값이 [ $# -gt 0 ] (인자의 개수가 0 보다 큰경우) 가 아닐경우까지 계속 반복되는것을 볼 수 있다.

 

 3) loop 구문

반복구문 내에서 반복을 멈추거나, 계속할 수 있도록 하는 명령어이다.

3-1) break 

break (숫자) 로 구성되며. 반복구문중 break 를 만나면 해당 수에 맞는 위치의 반복끝 구문으로 이동시킨다.

while [조건1]
do
    statement1
    while [조건2]
    do
        statement2
        while [조건3]
        do
            statement3
            [조건4] && break Number
            statement4
        done  # break 1 ( 조건2로 시작 )
    done  # break 2 ( 조건 1로 시작 )
done  # break 3 ( 끝 )

 break는 반복구의 탈출용도로 활용된다.

3-2) continue 

continue (숫자) 로 구성되며. 반복구문중 continue 를 만나면 해당 수에 맞는 위치의 반복시작 구문으로 이동시킨다.

while [조건1] # continue 3
do
    statement1
    while [조건2] # continue 2
    do
        statement2
        while [조건3] # continue 1
        do
            statement3
            [조건4] && continue Nunber
            statement4
        done
    done
done

 * continue는 반복구를 다시 실행하기 위해 활용된다.

3-3) 로그 모니터링 ( continue 활용 )

 로그 모니터링하는 파일을 제작해보자 부정적인 단어가 로그에 새롭게 쌓일경우 root에게 메일을 관련내용으로 보내는 프로그램이다.

#!/bin/bash

LOG1=/var/log/messages
TMP1=/tmp/.tmp1
TMP2=/tmp/.tmp2
TMP3=/tmp/.tmp3

egrep -i 'warn|fail|error|crit|alert|emerg' $LOG1 > $TMP1

while   true
do
    sleep 10
    egrep -i 'warn|fail|error|crit|alert|emerg' $LOG1 > $TMP2
    diff $TMP1 $TMP2 > $TMP3 && continue
    mailx -s "[ WARNING ] $LOG1" root < $TMP3
    egrep -i 'warn|fail|error|crit|alert|emerg' $LOG1 > $TMP1
done

3-4) FTP server 와 유사한 프로그램

FTP 처럼 입력을 받으면 그의 값이 출력되는 프로그램을 만들어보자

#!/bin/bash

HELP() {
cat <<EOF
Commands may be abbreviated.  Commands are:

!		debug		mdir		sendport	site
$		dir		mget		put		size
account		disconnect	mkdir		pwd		status

EOF
}

QUIT() {
    echo " Good Bye FTP server ^^"    
    exit 0
}

while true
do
    echo -n "ftp> "
    read CMD
    case $CMD in
        'help') HELP;;
        'quit') QUIT;;
        *) ?INvalid command
    esac
done

위처럼 while true 를 통해 무한반복되는 반복문을 통해 원하는 값을 입력시 원하는 결과가 출력되도록 case 구문과 함깨 활용하면 ftp server 와 유사한 파일을 제작할 수 있다.

 [참고] for, while 비교

예제 1)
#!/bin/bash

FTPUSERS=/etc/vsftpd/ftpusers

for NAME in $(cat $FTPUSERS | egrep -v '^$|^#')
do
    echo $NAME
done

예제 2)
#!/bin/bash

FTPUSERS=/etc/vsftpd/ftpusers

cat $FTPUSERS | egrep -v '^$|^#' | while read SARAM
do
    echo $SARAM
done

 위 두 내용은 동일한 결과값을 출력한다, 예제2의 경우 입력값 출력값 (IO) 를 파이프를 활용해서 받은걸 확인할 수 있다 이런걸 IO Redirection 이라고 한다

[root@linux200 ~/bin]# cat saram.txt 
user01	file1
user02	file2
user03	file3
# 이 내용을 갖고있는 파일을 쪼개보자

예제1)
#!/bin/bash

for i in $(cat saram.txt)
do
    echo $i
done

[root@linux200 ~/bin]# test.sh
user01
file1
user02
file2
user03
file3


예제2)
#!/bin/bash
cat saram.txt | while read SA FI
do
    echo "$SA $FI"
done

[root@linux200 ~/bin]# test.sh 
user01 file1
user02 file2
user03 file3

위 예제를 보면 for 구문의 경우는 값을 하나하나 쪼개서 쓰긴 어렵다, 이럴경우에는 while 구문을 활용할 시 파일 내 값을 하나하나 쪼개쓰는게 더욱 유리하다.