UNIX SHELL PROGRAMING 1

2013. 5. 21. 14:0199. 정리전 - IT/13. Unix 얇팍지식

 

shell, kill, while, for, if

#
# 입력 string을 포함하는 프로세스 죽이기

for pno in `ps | grep $1 | grep -v "grep"`
do
        echo $pno
        kill -9 $pno
done

 

#
# 입력한 시간까지 10분 간격으로 명령어 수행하기

if [ "$1" = "" ]; then end="2300"; else end=$1; fi
while :
do
        tim=`date +"%H%M"`
        echo `date +"%Y-%m-%d %H:%M:%S"`
        if [ "$tim" -ge "$end" ]; then
                (
                        cd {directory}
                        {command1}
                        {command2}
                )
                exit
        fi
        sleep 600
done 

 

shell, kill, while, for, if

#
# 입력된 모든 파일이 생길때까지 sleep

#
# 8초 주기로 완료 여부를 확인하며
# 완료된 목록은 반전으로 보임
#
# 아래와 같이 병렬로 여러개의 프로세스를 수행할때 유용

# for fnm in a b c d e f g
# do
#   (
#   {command} > result.$fnm.log
#   touch $fnm
#   ) &
# done
# sleepuntil a b c d e f g
# rm -f a b c d e f g

lst=$*

fnd=""
while [ "$fnd" = "" ]
do
    fnd="1"
    #
    echo " state : \c"
    for arg in $lst
    do
        if [ -f "$arg" ]; then
            echo "\033[7m$arg\033[0m \c"
        else
            echo "$arg \c"
            fnd=""
        fi
    done
    echo "\033[K\033[160D\c"
    if [ "$fnd" = "" ]; then sleep 8; fi
done
echo "\033[K\c"

 

sqlplus

#
############################################################
# sqlplus을 통해 출력된 값을 awk 프로그램을 통해 레코드만 출력
# . 변수를 이용하여 동적 query가 가능
# . 본 예는 record 가 2줄에 걸쳐 출력되는 경우임.
(
    echo "alter session set nls_date_format='YYYY/MM/DD HH24:MI:SS';"
    echo "set feedback off"
    echo "set head off"
    echo "set linesize 1000"
    echo "select a,b,c,d from ... ;"
) | sqlplus <id>/<password> | grep -v "^$" | nawk '
    BEGIN { mod=0; }
    mod==0&&/^SQL>/ { mod=1; next; }
    mod==1&&/^SQL>/ { mod=2; next; }
    mod==1 {
     if($2=="") printf "%s ",$1; else printf "%s %s %s\n",$1,$2,$3;
     next;
    }
'

 

 

ftp

#
############################################################
# 리모트 서버의 file을 ftp를 통해 가지고 오는 script
# 위와 마찬가지로 변수의 directory나 file을 가져옴
directory="simth"
filename="j.p.smith"
(
    echo "user <id> <password>"
    if [ "$opt" = "u" ]; then
        echo "cd /user/home/$directory"
        echo "mget <filename>.log"
    else
        echo "cd /tmp/log"
        echo "get $filename.log"
    fi
    echo "bye"
) | ftp -ni <ip_address> > /dev/null 2>&1

 

expr

#
############################################################
# record file에서 값을 가져와 차례로 수행하기
for usr in `cat /etc/passwd | awk -F':' '{ print $1; }'`
do
 (
  cd ~$usr
  du -s .
 )
done > diskusage.log
#
# 또는 record가 복잡할 경우
#
lno=1
lns=`head -1 /etc/passwd`
while [ "$lns" != "" ]
do
    usr=`expr "$lns" : '\([^:]*\).*$'`
    uid=`expr "$lns" : '[^:]*:[^:]*:\([^:]*\).*$'`

    # action

    echo "$usr's uid = $uid"
    #
    lno=`expr $lno + 1`
    lns=`tail +$lno /etc/passwd | head -1`
done

 

od, awk

# od 명령어의 -c 와 -x 옵션을 동시에 사용하는 효과

# - HEX 값과 출력이 가능한 ascii 글자를 같은 라인에 출력

# - shell은 다른 unix 명령어들의 제어와 간단한 데이터 처리를 위한 환경이라면,

#    awk는 record 형식의 데이터를 처리하는데 목적을 두는 프로그램이다

 

fnm=$1
if [ "$fnm" = "" ]; then echo "$0 <file_name>"; exit; fi
if [ ! -f "$fnm" ]; then echo "\"$fnm\" not exists"; exit; fi

od -v -x $fnm | nawk '
    BEGIN { hex="0123456789abcdef"; }
    {
        printf "%s ",$0;
        for(idx=NF+1;idx<10;idx++) printf "     ";
        for(idx=2;idx<=NF;idx++){
            for(pos=1;pos<=3;pos+=2){
                val=((index(hex,substr($idx,pos+0,1))-1)*16)+(index(hex,substr($idx,pos+1,1))-1)
                if(val>=32&&val<=127){
                    printf "%c",val;
                }else{
                    printf ".";
                }
            }
        }
        printf "\n";
    }
'

# 출력 예

0000000 2321 2f75 7372 2f62 696e 2f73 680a 0a66 #!/usr/bin/sh..f
0000020 6e6d 3d24 310a 6966 205b 2022 2466 6e6d nm=$1.if [ "$fnm
0000040 2220 3d20 2222 205d 3b20 7468 656e 2065 " = "" ]; then e
0000060 6368 6f20 2224 3020 3c66 696c 655f 6e61 cho "$0 <file_na
0000100 6d65 3e22 3b20 6578 6974 3b20 6669 0a69 me>"; exit; fi.i
0000120 6620 5b20 2120 2d66 2022 2466 6e6d 2220 f [ ! -f "$fnm"
0000140 5d3b 2074 6865 6e20 6563 686f 2022 5c22 ]; then echo "\"
0000160 2466 6e6d 5c22 206e 6f74 2065 7869 7374 $fnm\" not exist

 

nawk, awk

# nawk 프로그램의 다양한 예제입니다.

# nawk는 awk의 확장된 버젼으로 function이 추가 되었고 처리 할 수 있는 record 크기가 큽니다.

 

##############################

# 기본 구조

# - BEGIN 과 END는 파일 처리 전후에 수행

# - <pattern>은 'if' 의 조건과 같은 개념으로 해당 조건의 line(record) 만 처리

# - 조건이 없을 경우 모든 line  수행

# - 하나의 라인이 여러 조건이 맞을 경우 여러번 처리 될 수 있음 (next; 명령어를 쓰면 다음 레코드 처리 참조)

nawk '

  function

  BEGIN { <statement;> }

  <pattern> { <statement;> }

  <pattern> { <statement;> }

  END { <statement;> }

'

 

##############################

# shell 과 연동

# - wc 나 ps 등 일부 명령어의 경우 출력시 white space가 앞뒤로 붙는 경우가 있다.

#    단지 값만 원할 경우 유용하게 사용. shell 변수에 white space 가 포함되면 처리하기 귀찮음

lineno=`wc -l | nawk '{ print $1 }'`

 

##############################

# shell의 변수 사용하기

# - shell의 변수를 사용하면 더욱 강력한 프로그래밍이 가능

# - [']를 ["]로, ["] 는 [\"]로, [$]를 [\$]로 바꾸고 shell 변수는 [$var] 형식으로 표현

#    $var를 단순 대치하는 형식으로 string type으로 쓸경우 \"$var\"로 잘 씌울것~

for fnm in `ls a*`

do

    nawk " /$fnm/ { print \"$fnm\",substr(\$6,2,3); } " $fnm

done

 

##############################

# field 구분

# - nawk,awk의 기본 field 구분은 while space (space,tab)이나 다른 구분자로 구분하고자 할 경우

# - 예 : /etc/passwd file에서 ":"를 구분자로 할경우

nawk -F":" ' { } '

 

##############################

# state machine 방식의 처리

# - 정형화 되지 않은 파일이나 pattern이 있을때 유용 (로그 및 result 파일)

# - 아래는 "SQL>"로 시작하는 라인이 있을때까지 라인을 무시하다가

       다음 라인 부터 처리. 다시 "SQL>"이 나오면 입력 라인 무시

# - 글 "UNIX Shell 프로그래밍 (sqlplus,ftp,expr)" 참조

nawk '

  BEGIN { mod=0; }
  mod==0&&/^SQL>/ { mod=1; next; }    # next에 의해 아래 부분을 수행하지 않고 다음 라인을 수행
  mod==1&&/^SQL>/ { mod=2; next; }
  mod==1 {    # mod가 1일 경우에만 수행
   if($2=="") printf "%s",$1; else  printf " %d %s %5.2f\n",int($1*1000),$2,$3;
   next;
  }
'

 

statement

##############################

# statement 모음

# - 기본적으로 파일 이외에도 pipe 입력 가능

# - c 언어와 비슷

head -200  <파일명> | nawk '

 

    # 사용자 function 정의 하기

  function myfunction() {    #nawk만 지원

     <statement;>        # 기본적으로 변수는 global로 사용됨

    return myvalue;             # return 이 없을 경우 일반 procedure...

  }


  {

    # 사용자 function call 하기

    val=myfunction();

 

    # "." 이나 " "가 연속으로 있을 경우 newline으로 출력

    gsub("[\.][\.]+","\n",str);    # nawk만 지원
    gsub("[ ][ ]+","\n",str);
    printf "%s",str;

 

    # record의 마지막 10개 field를 array에 입력 
    for(idx=NF-9;idx<=NF;idx++) myarray[idx-(NF-9)]=$(idx);

 

    # "A"에서 "D" 자사이의 string  ($0 = 전체 라인)

    print substr($0,index($0,"A"),index(substr($0,index($0,"A")),"D")-index($0,"A"));

  }

 

  # pattern 의 예 변수 mod가 1이고 첫 field가 "pattern" 일경우

   mod==1&&$1=="pattern" {  <statement;> }


  # "pattern"을 포함 할 경우

   /pattern/ {  <statement;> }


  # 라인이 "patt"으로 시작하거나 "tern"으로 끝날 경우
   /^patt/||/tern$/ {  <statement;> }

 

 

background, grouping

프로그램 또는 프로세스를 background로 실행하려면 명령어의 뒤에

'&' 기호를 이용하면 되는데, telnet 접속을 끊으면 background로

실행되던 프로세스가 같이 죽는 경우가 있다.

 

이런한 경우에는 shell 프로그램 내에서 원하는 명령어를 background로

실행하게 하고 해당 shell 프로그램을 다시 background로 실행하면

가능한 경우가 있다.

 

아래는 background 처리를 위한 여러가지 방식의 예를 기술 해  보았다.

 

예)

. 다음과 같은 mybackground.sh 프로그램이 있을 경우

#!/usr/bin/sh

cd $i

for myfile in *

do

~/bin/myprocess.sh $myfile

done

# ~ = 로그인 홈 폴더를 의미함

 

. 단순하게 background로 수행

#!/usr/bin/sh

 

~/bin/mybackground.sh /home/myfolder1 &

 

. 별도의 mybackground.sh 프로그램을 쓰지 않고 처리하는 경우

#!/usr/bin/sh

(

cd /home/myfolder1

for myfile in *

do

~/bin/myprocess.sh $myfile

done

) &

# 괄호를 빠져나오면 현재 폴더로 다시 돌아옴.

# 여러 폴더를 이동하면서 작업하는 프로그램의 경우 유용

 

. 어려 폴더를 동시에 수행하는 경우 

#!/usr/bin/sh

~/bin/mybackground.sh /home/myfolder1 &

~/bin/mybackground.sh /home/myfolder2 &

~/bin/mybackground.sh /home/myfolder3 &

 

# myprocess.sh가 무거울 경우 서버에 많은 부하를 줌

# 빠른 처리가 필요하지 않은 경우 권장하지 않음

 

. 여러 폴더를 차례로 수행하는 경우

#!/usr/bin/sh

(

~/bin/mybackground.sh /home/myfolder1

~/bin/mybackground.sh /home/myfolder2

~/bin/mybackground.sh /home/myfolder3

) &

 

 

 

for,sed,awk,grep를 이용하여 property 파일의 값을 일괄 수정하는 예제

java 프로그램을 여러 site에 설치 해야 할 일이 있어 만든 프로그램이다.

 

아래 프로그램의 예는 shell을 이용하여 java의 properties, xml, ini 파일
등을 일괄적으로 바꿔준다. 일단 어플리케이션 path를 일괄적으로
변경하는 예를 들었지만 같은 방식으로 ip, db 정보등을 변경할 수 있다.

 

아래 내용을 복사하여 사용 할때 tab("    ") 자와 esacpe code("^[") 주의

-------------------------------------------------------------
#!/usr/bin/sh

############################################
# 파일 명 : changeproperties.sh
############################################

dir=`dirname $0`
cmd=`basename $0`

opt=$1
ag1=$2
err=""

############################################
# default 어플리케이션 object
############################################
ap_home="/usr/home/application"

############################################
# check parameter & environment
############################################
if [ "$opt" = "" ]; then err="1"; fi

if [ "$opt" = "ap_home" ]; then
    if [ "$ag1" = "" ]; then ag1="$ap_home"; fi
fi

if [ "$err" = "1" ]; then
    #   vt100 화면 control 코드 사용. '^['자를 vi에서 입력하려면 <ctrl>-'v'-'['를 치면 됨
    echo "^[[1m[ USAGE ]^[[0m"
    echo ""
    echo "  ^[[1m$cmd ap_home <path>^[[0m"
    echo "  . sets/changes ^[[4mAPPLICATION^[[0m home directory"
    echo ""
    exit 0
fi

 

############################################
# process command for ap_home
############################################
if [ "$err" = "" -a "$opt" = "ap_home" ]; then

 

    ############################################
    # 일반 파일을 변경하는 예
    ############################################
    for fnm in myfile1.ini mufile2.ini
    do
        # 변경할 대상 파일명을 화면에 출력하는 부분
        echo "^[[4mmyfolder1/$fnm^[[0m"
       
        # '[DIRECTORY]' 에 해당하는 값들만 변경하도록 mod 변수 사용
        nawk "
            BEGIN               { mod=1; }
            /^\[DIRECTORY]/     { print \$0; mod=0; next; }
            mod==0&&/^gen_out/  { print \"gen_out = $ap_home/myfolder1/gen_out/logs/\"; next; }
            mod==0&&/^intp_in/  { print \"intp_in = $ap_home/myfolder1/intp_in/logs/\"; next; }
            mod==0&&/^sndbak/   { print \"sndbak = $ap_home/myfolder1/sndbak/\"; next; }
            mod==0&&/^MA/       { print \"MA = $ap_home/myfolder1/magreement/\"; next; }
            mod==0&&/^IA/       { print \"IA = $ap_home/myfolder1/iagreement/\"; next; }
            mod==0&&/^BOARD/    { print \"BOARD = $ap_home/myfolder1/work/dir_board/\"; next; }
            mod==0&&/^FTP_LOG/  { print \"FTP_LOG = $ap_home/myfolder/work/server_log/\"; next; }
            mod==0&&!/^\[/      { print \$0; next; }
            /^\[/               { print \$0; mod++; next; }
                                { print \$0; }
        " myfolder/$fnm > $fnm.tmp

 

        # 변경된 내역을 화면에 출력하는 부분 (앞의 white space를 tab 1개로 변경)
        grep $ap_home/myfolder $fnm.tmp | nawk ' { prn=$0; sub("^[   ]*","   ",prn); print prn; }; '
        mv $fnm.tmp myfolder/$fnm
    done


    ############################################
    # 여러 위치에 있는 jar 파일 내의 환경 파일을 변경하는 경우
    ############################################
    for jnm in myfolder1/myJar1 myfolder1/myJar2 myfolder2/WEB-INF/lib/myJar2
    do
        # 변경 대상 파일 명을 system.properties 파일에서 먼저 찾음
        jar -xf $jnm.jar system.properties
        fnm=""
        fnm=`sed -e "s/[    ^M]*//g" system.properties | nawk -F= ' /^SettingFileName=/ { print $2; } '`

 

        if [ "$fnm" != "" ]; then
            echo "^[[4m$jnm.jar/$fnm^[[0m"
            jar -xf $jnm.jar $fnm
            nawk "
                /^RevIC/    { print \"RevIC=$ap_home/myfolder/intp_in/\"; next; }
                /^SndIC/    { print \"SndIC=$ap_home/myfolder/gen_out/\"; next; }
                /^Sam/      { print \"Sam=$ap_home/myfolder/sam/\"; next; }
                /^Ic/       { print \"Ic=$ap_home/myfolder/ic/\"; next; }
                /^Batch/    { print \"Batch=$ap_home/myfolder/batch/\"; next; }
                            { print \$0; }
            " $fnm > $fnm.tmp


            grep $ap_home/myfolder $fnm.tmp | nawk ' { prn=$0; sub("^[  ]*","   ",prn); print prn; };
            mv $fnm.tmp $fnm
            jar -uf $jnm.jar $fnm
            rm -rf system.properties $fnm
        fi
    done

fi

------------------------------------------------------------

 

서버 모니터링

아래의 내용을 모니터링 할 수 있는 간단한 shell 스크립트 입니다.

ㅇ CPU 및 메모리 사용율 - 서버의 종합적인 상태

ㅇ 프로세스 수행 개수  - 평균 수에서 변화가 커지면 시스템에 먼가가 일어나고 있음

ㅇ 네트워크 connection 개수 - web 서버나 DB서버 에서 사용자 접속 상태 파악 가능

ㅇ 주요 디스크 사용율 - 로그로 인한 디스크 풀 주의!!

 

#!/usr/bin/sh 

# SOLARIS 10

# prtdiag는 root에서만 수행 가능. 일반 user로 수해하려면 값을 명시

 

logdir=/log/mylog

sysmem=`prtdiag | grep "Memory size:" | awk '{ print $3' ; }'`  

while :

do

dat=`date +"%y%m%d"`

tme=`date +"%y%m%d%H%M"`

 

# 프로세스 개수

curprc=`ps -e | wc -l | awk ' { print $1; } '`

 

# 네트워크 connection 수

curnet=`netstat -n | grep ESTABLISHED | wc -l | awk '{ print $1; } '`

 

# CPU & Memory

vmstat 1 2 | tail -1 | awk " {

printf \"$tme CPU %d MEM %d PRC $curprc NET $curnet\\n\",

100-\$NF,

100-((\$5/1024)*100/$sysmem);

}  >> $logdir/svrmon_$dat.log

# DISK

(

for curdir in /src /log

do

df -h $curdir | grep "[0-9]G" | grep "[0-9]%" | awk " {

print \"$tme DISK $curdir %s\\n\",\$5;

} "

done

) >> $logdir/dskmon_$dat.log

 

sleep 60

done 

 

 

위 프로세스를 background로 수행시키고 tail -f /log/mylog/... 명령어로 모니터링 하면 됩니다.

 

 

Oracle 모니터링

아래의 내용을 모니터링 할 수 있는 간단한 shell 스크립트 입니다.

ㅇ LOCK 수 - DB가 죽어가는지 확인 가능

 

아래의 예시를 활용해서 다른 많은 모니터링도 가능하지만 DBA가 아닌

입장에서는 DB와 관련되어 서버 Load, Transaction Load 과 LOCK이 쌓여가는지만 확인하면

보면 충분 한듯합니다. DB 서버 상태는 서버 모니터링에서 Transaction 관련해서는 WAS에서의

Active Data Connection 개수를 모니터링 하면 됩니다.

 #!/usr/bin/sh

# SOLARIS 10

# Oracle Admin 계정으로 수행

logdir=/log/mylog

 

while :

do

dat=`date +"%y%m%d"`

tme=`date +"%y%m%d%H%M"`

 

(

echo "select count(distinct(lk.sid)) from v\$lock lk where lk.type in ('TX','TM','UL');"

echo "exit"

) | sqlplus "/as sysdba" | awk "

BEGIN { mod=0; }

mod==0&&/^COUNT.DISTINCT./ { mod=1; next; }

mod==1&&/^--------/ { mod=2; next; }

mod==2 { printf \"$tme LOCK %d\\n\", \$1; mod=3; next; }

" >> $logdir/dbmod_$dat.log

 

sleep 60

done 

 
마찬가지로 tail -f 명령어로 보면 됩니다.

 

 

JEUS 모니터링

아래의 내용을 모니터링 할 수 있는 간단한 shell 스크립트 입니다.

ㅇ 콘테이너별 사용하는 DataSource Connection 수 - 사용되어지고 있는 DB connection 수

ㅇ 콘테이너별 사용하는 Thread 수 - 처리하고 있는 Transaction  수

 

어플리케이션 튜닝 상태에 따라 조금 다르겠지만 총 Thread 수는 TPS의 약 10일 수준으로 나타나고

DataSource 수는 Thread 수와 비슷하게 나오는데 이들 정보를 이용하여 콘테이너의 구성 변경 이나

DBMS의 connection resource 및 부하 시 활용 할 수 있습니다.

#!/usr/bin/sh
# SOLARIS 10

logdir=/log/mylog

hostnm=`hostname`
tmpdir=/tmp


while :
do


dat=`date +"%y%m%d"`

tme=`date +"%y%m%d%H%M"`

# ja : jeusadmin

# dsinfo : Datasource 정보 

# ti : Thread 정보

(

sleep 2

echo "dsinfo -active"

sleep 2

echo "ti"

sleep 2

echo "exit"

) | ja > $tmp/jeusmon.tmp

 

# container별, datasource별, active 및 max connection 수 출력

cat $tmp/jeusmon.tmp | /usr/xpg4/bin/grep -e "true" -e "engine container" |

/usr/xpg4/bin/awk "

/container/ {

connme=\$NF;

if(\$1==\"Connection\"){

connme=substr(con,12);

connme=substr(con,1,length(con)-1);

}else{

connme=substr(con,21);

connme=substr(con,1,length(con)-1);

}

connme=toupper(connme);

}

/true/ {

 print \"$tme DS=%s ACT=%d MAX=%d\\n\",$connme, \$2, \$8, \$14;

}

" >> $logdir/jdimon_$dat.log

 

# container별 active  및 max thread 수 출력

cat $tmp/jeusmon.tmp | /usr/xpg4/bin/grep -e "\[total :" -e "ContainerName" |

/usr/xpg4/bin/awk "

BEGIN {

prenme=\"\";

idlcnt=0;

maxcnt=0;

}

/ContainerName/ {

connme=substr(\$4,length(\"$hostnm\")+2);

connme=toupper(connme);

if(prenme!=\"\"&&prenme!=connme){

printf \"CON=%s ACT=%d MAX=%d\\n\",

prenme, maxcnt-idlcnt,maxcnt;

idlcnt=0;

maxcnt=0;

}

prenme=connme;

}

/total :/ {

maxcnt=maxcnt+\$3;

idlcnt=idlcnt+\$9;

}

END {

printf \"CON=%s ACT=%d MAX=%d\\n\",prenme, maxcnt-idlcnt,maxcnt;

}

" >> $logdir/jtimon_$dat.log

 

sleep 60

done

 

거래 내용을 모니터링하려면 APM 이나 DB 모니터링 툴이 있어야 하겠죠?

그리고 모니터링 관련 글에서 내용들을 tail -f 로 보려면 전체 현황 파악이 힘듭니다.

nmon이나 top 명령어처럼 전체 상황들을 dashboard 처럼 한 화면 내에서 모니터링 대상 서버별로

모니터링 항목들이 표처럼 출력 할 필요가 있습니다.