티스토리 뷰

시스템 서버

[linux] Shell Script 공부 4일차 - Chapter 4

달리는개발자 2011. 10. 30. 21:44
Chapter 4 | Advanced Shell Scripting Commands

다음과 같은 고급 쉘 스크립 및 명령어를 배워보자
1. Functions
2. User Interface
3. Conditional execution
4. File Descriptors
5. traps
6. Multiple command line args handling etc

/dev/null - 프로그램 출력을 원하지않을 때 사용
구문
command > /dev/null

예제
$ ls > /dev/null


지역 그리고 전역 쉘 변수 (export command)

지역변수
예제
$ vech=Bus
$ echo $vech
Bus
$ /bin/bash             //메모리에 두번째 쉘을 로딩.... 이전 쉘의 변수들은 모두 무시됨.
$ echo $vech
빈 라인이 출력됨....
$ vech=Car
$ echo $vech
Car
$ exit                      // 두번째 쉘을 나오고 첫번째 쉘로 돌아간다.
$ echo $vech
Bus

전역변수
전역변수를 사용함으로써 이전 쉘의 변수를 새 쉘에 변수에 복사할 수 있다.
구문
export variable1,variable2, ....... variableN
예제
$ vech=Bus
$ echo $vech
Bus
$ export vech
$ /bin/bash
$ echo $vech
Bus
$ exit
$ echo $vech
Bus

조건 연산자(&& 와 ||)
구문
command1 && command2
command1이 성공이면 command2 실행

command1 || command2
command1이 성공이 아니면 command2 실행

command1 && command2 || command3
command1이 성공이면 command2 실행
command1이 성공이 아니면 command3 실행

예제
[root@smsc tmp]# cat dsh.sh && echo "Contents of file displayed successfully" || echo
"Nothing to display. No such file present"
cat: dsh.sh: No such file or directory
Nothing to display. No such file present
[root@smsc tmp]# cat dsh.sh && echo "Contents of file displayed successfully" || echo
"Nothing to display. No such file present"
echo “Hello……….”
Contents of file displayed successfully

입출력 리다이렉션과 파일 기술자(file descriptor)

파일기술자 란?
 - 운영체계에서 파일을 사용할 때 각 파일에 대한 정보를 유지하는 기억 장치의 한 영역, 또는 그 정보.

Standard File File Descriptors number  Use  Example 
 stdin  0  as Standard input  Keyboard
 stdout  1  as Standard output  Screen
 stderr  2  as Standard error  Screen

By default in Linux every program has three files associated with it, (when we start our program
these three files are automatically opened by your shell). The use of first two files (i.e. stdin and
stdout), are already seen by us. The last file stderr (numbered as 2) is used by our program to
print error on screen.

구문
file-descriptor-number>filename

예제 : bad_file_name111이란 파일이 없다고 가정한다.
$ rm badfile_name111
rm: cannot remove `bad_file_name111': No such file or directory

$ rm badfile_name111 > er
rm: cannot remove `bad_file_name111': No such file or directory
만약 당신이 $ cat er로 파일을 보고자하면 출력은 error device로 보내졌기때문에 그 파일은 없고,
에러 출력을 er 파일에 복사할 수 없다. 이 문제를 해결하기 위해 다음과 같이 실행한다.
$ rm badfile_name111 2>er
주의 : 2와 > 를 붙인다. 2>er은 표준에러출력을 파일로 보낸다.
2는 stderr 파일의 기본 번호(파일 기술자 번호)이다.

예제
$ cat > demo.sh
if [ $# -ne 2 ]
then
   echo "Error : Number are not supplied"
   echo "Usage : $0 number1 number2"
   exit 1
fi
ans=`expr $1 + $2`
echo "Sum is $ans"

$ chmod 755 demo.sh
Error : Number are not supplied
Usage : ./demo.sh number1 number2
$ ./demo.sh > err 
$ cat err
Error : Number are not supplied
Usage : ./demo.sh number1 number2 
$ ./demo.sh 5 7
Sum is 12

에러 메시지를 stdout이 아닌 stderr로 변경하기위해 다음과 같이 쉘스크립트를 수정한다.
echo "Error: Number are not supplied" 1>&2
echo "Usage : $0 number1 number2" 1>&2

echo문의 끝의 1>&2는 standar output을 standard error device로 가르킨다.

구문
from>&destination

Functions
프로그램이 복잡할 때 부분으로 나뉘어진 개체들
1. 명령어들의 연속이다.
2. function은 특정 작업이나 단순한 작업을 수행한다.

구문
function-name ()
{
   command1
   command2
   command3
   ....
   ...
   commandN
   return
}

예제
$ SayHello()
{
   echo "Hello $LOGNAME, Have nice computing"
   return
}

실행
$ SayHello
Hello abhishek, Have nice computing.

위의 예제는 오직 현재 세션을 위해 생성되기 때문에 컴퓨터를 다시 시작하는 경우 잃어버린다.
이 문제를 해결하기 위해 function을 /etc/bashrc file에 추가한다.
function을 추가하기 위해선 root로 로그인해야 한다.
vi 로 /etc/bashrc 파일을 열고 파일의 마지막으로 가서(shift+G) today() function을 입력한다.
# vi /etc/bashrc
# At the end of file add following in /etc/bashrc file
#
# today() to print formatted date
#
# To run this function type today at the $ prompt
# Added by Abhishek to show function in Linux
#
today()
{
echo This is a `date +"%A %d in %B of %Y (%r)"`
return
}

수정 후
# cat /etc/bashrc 
# /etc/bashrc 
# System wide functions and aliases
# Environment stuff goes in /etc/profile
# For some unknown reason bash refuses to inherit
# PS1 in some circumstances that I can't figure out.
# Putting PS1 here ensures that it gets loaded every time.
PS1="[\u@\h \W]\\$ "
#
# today() to print formatted date
#
# To run this function type today at the $ prompt
# Added by Abhishek to show function in Linux
today()
{
echo This is a `date +"%A %d in %B of %Y (%r)"`
return
}

today란 function은 시스템의 어느 사용자든 쓸 수 있다.
특정 사용자만 쓸 수 있는 function을 만들고 싶다면 다음과 같이 해당 사용자의 home directory의 .bashrc
파일을 열어서 수정한다.
# vi .bashrc
OR
# mcedit .bashrc
.bashrc file의 마지막 부분에 다음과 같이 추가한다.
SayBuy()
{
echo "Buy $LOGNAME ! Life never be the same, until you login again!"
echo "Press a key to logout. . ."
read
return
}


로그아웃시에 어떤 메시지를 보여주고 싶다면 해당 사용자의 home directory에
.bash_logout 파일을 만들고 다음과 같이 입력한다.

echo "Buy $LOGNAME, Press a key. . ."
read

로그아웃을 한 경우에 메시지를 보여주고 key를 누르면 로그아웃이 된다.

왜 function을 쓸까?
1. 많은 시간을 절약한다.
2. 같은코드의 재작성을 피한다.
3. 프로그램을 만들기 쉽다.
4. 프로그램 유지보수가 매우 쉽다.

bashrc의 rc는 무슨 뜻 일까?
" run commands " 의 약어이고 시작 명령어 정보가 포함된 파일에 사용하곤한다.
1965년 MIT Compatible Time-Sharing System(CTSS) system의 runcom 시설로 부터 유래됐다고 한다.
더 자세한 정보는 위키피디아를 참조하세요 ^^;;;

출처
http://en.wikipedia.org/wiki/Run_Commands

사용자 정의 함수에 파라미터 보내기

다음과 같이 command line 옵션처럼 파라미터를 보낼 수 있다.
function-name arg1 arg2 arg3 argN

예제
$ vi pass
function demo()
{
   echo "All Arguments to function demo(): $*"
   echo "First argument $1"
   echo "Second argument $2"
   echo "Third argument $3"
   return
}
#
# Call the function
#
demo -f foo bar

$ chmod +x pass
$ ./pass
All Arguments to function demo(): -f foo bar
First argument -f
Second argument foo
Third argument bar

command line 인자나 옵션을 참조하는 것과 같은 방법으로 function에 파라미터를 참조할 수 있다.

예제2
$ vi pass1
function cal()
{
   n1=$1
   op=$2
   n2=$3
   ans=0
   if [ $# -eq 3 ]; then
      ans=$(( $n1 $op $n2 ))
      echo "$n1 $op $n2 = $ans"
      return $ans
   else
      echo "Function cal requires at least three args"
   fi
   return
}
cal 5 + 10
cal 10 - 2
cal 10 / 2
echo $?

$ chmod +x pass1
$ ./pass1
5 + 10 = 15
10 - 2 = 8
10 / 2 = 5
5

User Interface and dialog utility-Part I
좋은 프로그램 또는 쉘 스크립트는 반드시 사용자들과 소통해야한다.

$ cat > userinte
#
# Script to demo echo and read command for user interaction
#
echo "Your good name please :"
read na
echo "Your age please :"
read age
neyr=`expr $age + 1`
echo "Hello $na, next year you will be $neyr yrs old."

$ chmod 755 userinte
$ ./userinte
Your good name please :
Abhishek
Your age please :
25
Hello Abhishek, next year you will be 26 yrs old

당신은 사용자와 소통하는 메뉴를 만들 수 있고 메뉴를 선택을 요청하고 선택된 메뉴에 따라 적당한
동작을 취하게 만든다.

$ cat > menuui
#
# Script to create simple menus and take action according to that selected
# menu item
#
while :
 do
    clear
    echo "-------------------------------------"
    echo " Main Menu "
    echo "-------------------------------------"
    echo "[1] Show Todays date/time"
    echo "[2] Show files in current directory"
    echo "[3] Show calendar"
    echo "[4] Start editor to write letters"
    echo "[5] Exit/Stop"
    echo "======================="
    echo -n "Enter your menu choice [1-5]: "
    read yourch
    case $yourch in
      1) echo "Today is `date` , press a key. . ." ; read ;;
      2) echo "Files in `pwd`" ; ls -l ; echo "Press a key. . ." ; read ;;
      3) cal ; echo "Press a key. . ." ; read ;;
      4) vi ;;
      5) exit 0 ;;
      *) echo "Opps!!! Please select choice 1,2,3,4, or 5";
         echo "Press a key. . ." ; read ;;
    esac
done

User Interface and dialog utility-Part II
기본적으로 diaglog utility가 설치되어있지 않기때문에 diaglog utility를 사용하는 프로그래밍 전에 
diaglog utility를 설치해야한다.

Debian 또는 Ubuntu Linux인 경우
$ sudo apt-get update
$ sudo apt-get install dialog

CentOS 또는 Redhat Linux인 경우
$ yum install dialog

출처
http://bash.cyberciti.biz/guide/Bash_display_dialog_boxes

구문을 이해하기전에 바로 실습해보자
$ cat > dia1
dialog --title "Linux Dialog Utility Infobox" --backtitle "Linux Shell Script
Tutorial" --infobox "This is dialog box called infobox, which is used
to show some information on screen, Thanks to Savio Lam and
Stuart Herbert to give us this utility. Press any key. . . " 7 50 ; read

$ chmod +x dia1
$ ./dia1



7은 box의 높이이고, 50은 box의 너비이다.

구문
 dialog --title {title} --backtitle {backtitle} {Box options} 
        Box options은 다음과 같다.
        --yesno      {text}  {height} {width}
        --msgbox     {text}  {height} {width}
        --infobox    {text}  {height} {width}
        --inputbox   {text}  {height} {width} [{init}]
        --textbox    {file}  {height} {width}
        --menu       {text}  {height} {width} {menu} {height} {tag1} item1}...

Message box (msgbox) using dialog utility
$ cat > dia2
dialog --title "Linux Dialog Utility Msgbox" --backtitle "Linux Shell Script
Tutorial" --msgbox "This is dialog box called msgbox, which is used
to show some information on screen which has also Ok button, Thanks to Savio Lam
and Stuart Herbert to give us this utility. Press any key. . . " 9 50

$ chmod +x dia2
$ ./dia2


yesno box using dialog utility

$ cat > dia3
dialog --title "Alert : Delete File" --backtitle "Linux Shell Script
Tutorial" --yesno "\nDo you want to delete '/usr/letters/jobapplication'
file" 7 60
sel=$?
case $sel in
   0) echo "User select to delete file";;
   1) echo "User select not to delete file";;
   255) echo "Canceled by user by pressing [ESC] key";;
esac

$ chmod +x dia3
$ ./dia3


Input Box(inputbox) using dialog utility

$ cat > dia4
dialog --title "Inputbox - To take input from you" --backtitle "Linux Shell\
Script Tutorial" --inputbox "Enter your name please" 8 60 2>/tmp/input.$$
 
sel=$?
 
na=`cat /tmp/input.$$`
case $sel in
  0) echo "Hello $na" ;;
  1) echo "Cancel is Press" ;;
  255) echo "[ESCAPE] key pressed" ;;
esac
 
rm -f /tmp/input.$$

$ chmod +x dia4
$ ./dia4


User Interface using dialog Utility - Putting it all together
메뉴 아이템들은 다음과 같다.
Date/time - 현재 data/time을 보여준다
Calendar -  달력을 보여준다
Editor - vi Editor를 실행한다.

$ cat > smenu
#
#How to create small menu using dialog
#
dialog --backtitle "Linux Shell Script Tutorial " --title "Main\
Menu" --menu "Move using [UP] [DOWN],[Enter] to\
Select" 15 50 3\
Date/time "Shows Date and Time"\
Calendar "To see calendar "\
Editor "To start vi editor " 2>/tmp/menuitem.$$
menuitem=`cat /tmp/menuitem.$$`
opt=$?
case $menuitem in
                               Date/time) date;;
                               Calendar) cal;;
                               Editor) vi;;
esac
rm -f /tmp/menuitem.$$

$ chmod +x smenu
$ ./smenu


15 : 박스 높이
50 : 박스 폭
3  : 메뉴 높이
dialog utility의 문제는 작업 시 느리다는 점이다...

trap command (CTRL + C 등 처리)
$ cat > testsign
ls -R /

$chmod +x testsign
$ ./testsign

이때 ctrl + c를 누르면 실행 중인 스크립트가 종료될 것이다.
시스템에서 현재 실행 중인 모든 프로세스에 신호를 보낸다.

다음의 예제를 실행한다.
$ cat > testsign1
#
# Why to trap signal, version 1
#
Take_input1()
{
 recno=0
 clear
 echo "Appointment Note keeper Application for Linux"
 echo -n "Enter your database file name : "
 read filename
if [ ! -f $filename ]; then
  echo "Sorry, $filename does not exit, Creating $filename database"
  echo "Appointment Note keeper Application database file" > $filename
fi
echo "Data entry start data: `date`" >/tmp/input0.$$
#
# Set a infinite loop
#
while :
do
     echo -n "Appointment Title:"
     read na
     echo -n "time :"
     read ti
     echo -n "Any Remark :"
     read remark
     echo -n "Is data okay (y/n) ?"
     read ans
if [ $ans = y -o $ans = Y ]; then
    recno=`expr $recno + 1`
    echo "$recno. $na $ti $remark" >> /tmp/input0.$$
fi
echo -n "Add next appointment (y/n)?"
read isnext
 if [ $isnext = n -o $isnext = N ]; then
     cat /tmp/input0.$$ >> $filename
     rm -f /tmp/input0.$$
    return # terminate loop
 fi
done
}
#
#
# Call our user define function : Take_input1
#
Take_input1

1. 데이터베이스 파일명을 묻고 있으면 사용, 없으면 생성한다
2. tmp 디렉토리에 임시파일을 열고 오늘 날짜를 파일에 기록한다
3. 그리고 제목, 시간, 비고를 묻는 무한루프가 시작된다
4. 사용자에게 정보가 맞는지 확인하고 맞으면 임시파일에 기록한다
5. 추가할 레코드가 없으면 임시파일에 있던 기록들을 데이터베이스 파일에 기록하고 종료된다

지금의 문제는 CTRL + C를 눌렀을 경우 쉘 스크립트가 종료되고 임시파일이 tmp 디렉토리에 남겨진다는 것이다.
이 문제를 해결하기 위해 trap command를 사용해서 신호를 처리해야한다.

trap command 구문
trap {commands} {signal number list}
Signal Number When occurs
0 shell exit
1 hangup
2 interrupt (CTRL + C)
3 quit
9 kill (cannot be caught)

신호를 잡기위해 Take_inpu1 function 호출 전에 trap 문장을 추가한다.
trap command는 interrupt(CTRL + C)가 걸렸을 때 del_file()을 호출할 것이다
다음과 같다.

$ vi testsign1
#
# signal is trapped to delete temporary file , version 2
#
del_file()
{
  echo "* * * CTRL + C Trap Occurs (removing temporary file)* * *"
  rm -f /tmp/input0.$$
  exit 1
}
 
Take_input1()
{
recno=0
clear
echo "Appointment Note keeper Application for Linux"
echo -n "Enter your database file name : "
read filename
if [ ! -f $filename ]; then
  echo "Sorry, $filename does not exit, Creating $filename database"
  echo "Appointment Note keeper Application database file" > $filename
fi
echo "Data entry start data: `date`" >/tmp/input0.$$
#
# Set a infinite loop
#
while :
do
  echo -n "Appointment Title:"
  read na
  echo -n "time :"
  read ti
  echo -n "Any Remark :"
  read remark
  echo -n "Is data okay (y/n) ?"
  read ans
  if [ $ans = y -o $ans = Y ]; then
   recno=`expr $recno + 1`
   echo "$recno. $na $ti $remark" >> /tmp/input0.$$
  fi
  echo -n "Add next appointment (y/n)?"
  read isnext
  if [ $isnext = n -o $isnext = N ]; then
    cat /tmp/input0.$$ >> $filename
    rm -f /tmp/input0.$$
    return # terminate loop
  fi
done # end_while
}
#
# Set trap to for CTRL+C interrupt i.e. Install our error handler
# When occurs it first it calls del_file() and then exit
#
trap del_file 2
#
# Call our user define function : Take_input1
#
Take_input1

CTRL + C로 나가도 임시파일은 보이지 않는다.

The shift Command
현재값을 현재 위치에서 한단계 왼쪽 위치로 변경한다.
$1 = -f $2 = foo $3 = bar
일 때 shift command를 실행하면 다음과 같다
$1 = foo $2 = bar

예제
$ vi shiftdemo.sh
echo "Current command line args are: \$1=$1, \$2=$2, \$3=$3"
shift
echo "After shift command the args are: \$1=$1, \$2=$2, \$3=$3"

$ chmod +x shiftdemo.sh
$ ./shiftdemo -f foo bar
Current command line args are: $1=-f, $2=foo, $3=bar
After shift command the args are: $1=foo, $2=bar, $3=

지정한 만큼 위치를 변경할 수도 있다.
2단계를 바꾸고 싶으면 다음과 같이 실행한다.
shift 2

shift command를 어디에 사용할까?
적당한 command line 옵션이 있을 때 유용하다
옵션이 너무 많으면 작성하고 유지하기가 복잡한만큼 느리다

예제
$ vi convert
while [ "$1" ]
do
   if [ "$1" = "-b" ]; then
        ob="$2"
        case $ob in
          16) basesystem="Hex";;
           8) basesystem="Oct";;
           2) basesystem="bin";;
           *) basesystem="Unknown";;
        esac
       shift 2
   elif [ "$1" = "-n" ]
   then
      num="$2"
      shift 2
   else
      echo "Program $0 does not recognize option $1"
      exit 1
   fi
done
output=`echo "obase=$ob;ibase=10; $num;" | bc`
echo "$num Decimal number = $output in $basesystem number system(base=$ob)"

$ chmod +x convert
$ ./convert -b 16 -n 500
500 Decimal number = 1F4 in Hex number system(base=16)
$ ./convert -b 8 -n 500
500 Decimal number = 764 in Oct number system(base=8)
$ ./convert -b 2 -n 500
500 Decimal number = 111110100 in bin number system(base=2)
$ ./convert -b 2 -v 500
Program ./convert does not recognize option -v
$ ./convert -t 2 -v 500
Program ./convert does not recognize option -t
$ ./convert -b 4 -n 500
500 Decimal number = 13310 in Unknown number system(base=4)
$ ./convert -n 500 -b 16
500 Decimal number = 1F4 in Hex number system(base=16)

BC를 사용해서 화면에 변환된 숫자를 보여준다
echo 'obase=16; ibase=10; 64206' | bc

Getopts command
이 명령어는 스크립트에 보낸 인자가 올바른지 확인한다.
대개 while 문에서 사용한다.

구문
getopts {optstring} {variable1}

$ vi ani
#
# Usage: ani -n -a -s -w -d
#
#
# help_ani() To print help
#
help_ani()
{
  echo "Usage: $0 -n -a -s -w -d"
  echo "Options: These are optional argument"
  echo " -n name of animal"
  echo " -a age of animal"
  echo " -s sex of animal "
  echo " -w weight of animal"
  echo " -d demo values (if any of the above options are used "
  echo " their values are not taken)"
  exit 1
}
#
#Start main procedure
#
#
#Set default value for variable
#
isdef=0
na=Moti
age="2 Months" # may be 60 days, as U like it!
sex=Male
weight=3Kg
#
#if no argument
#
if [ $# -lt 1 ]; then
  help_ani
fi
while getopts n:a:s:w:d opt
do
  case "$opt" in
    n) na="$OPTARG";;
    a) age="$OPTARG";;
    s) sex="$OPTARG";;
    w) weight="$OPTARG";;
    d) isdef=1;;
    \?) help_ani;;
  esac
done
if [ $isdef -eq 0 ]
then
  echo "Animal Name: $na, Age: $age, Sex: $sex, Weight: $weight (user define mode)"
else
  na="Pluto Dog"
  age=3
  sex=Male
  weight=20kg
  echo "Animal Name: $na, Age: $age, Sex: $sex, Weight: $weight (demo mode)"
fi

$ chmod +x ani
$ ani -n Lassie -a 4 -s Female -w 20Kg
$ ani -a 4 -s Female -n Lassie -w 20Kg
$ ani -n Lassie -s Female -w 20Kg -a 4
$ ani -w 20Kg -s Female -n Lassie -a 4
$ ani -w 20Kg -s Female
$ ani -n Lassie -a 4
$ ani -n Lassie
$ ani -a 2

유효하지 않은 옵션들

옵션과 값사이에 공간이 없음
$ ani -nLassie -a4 -sFemal -w20Kg
$ ani -nLassie-a4-sFemal-w20Kg

-c 옵션이 없다
$ ani -n Lassie -a 4 -s Female -w 20Kg -c Mammal




반응형
댓글
반응형
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
«   2024/05   »
1 2 3 4
5 6 7 8 9 10 11
12 13 14 15 16 17 18
19 20 21 22 23 24 25
26 27 28 29 30 31
글 보관함