본문 바로가기

Academy I/Tech Academy

Unix 13 쉘 프로그램의 제어문

쉘 프로그램의 제어문 : select

1. select 제어문

    Korn Shell에서는 Bourne Shell에서는 제공하지 않는 쉘 프로그램에서
    요긴하게 사용할수 있는 선택메뉴를 자동적으로 작성할수 있도록 지원하는
    명령어를 제공한다. 이 명령어가 바로 select 이다.

    또한 Korn Shell은 select 명령어의 실행상의 제어를 위해 3가지의 쉘 변수
    즉, PS3, COLUMN 그리고 LINES 변수를 제공한다.
    PS3 변수는 선택메뉴에서 사용할 프롬프트를 재정의하는 기능을
    제공하며, COLUMN과 LINES 변수는 화면상에 메뉴를 표시할 위치를
    정의할 수 있는 기능을 제공한다. select 명령어의 형식은 for 명령어와
    대단히 유사하다. 다음은 select 제어문의 형식이다.

         select VAR in ARG1 ARG2 ... ARGn
         do
              command1
              command2
              ...
              commandN
         done

    select 명령어와 for 명령어는 select 명령어는 사용자가 선택메뉴상에
    나열된 항목중에서 선택한 하나의 항목에 대해 do와 done 사이에
    지정되어 있는 명령어들을 실행한다는 점을 제외하고는 매우 유사한
    작업을 실행한다. 또한 select 명령어는 break, exit 또는 return
    명령어를 만날때까지 반복하여 사용자 선택에 대한 명령어 실행을
    계속한다.

    다음은 select 명령어의 사용예이다.

  ┌───────────────────────────────────┐
  │sel                                                                   │
  ├───────────────────────────────────┤
  │PS3="choose one:"           # 선택메뉴에서 사용할 프롬프트를 선언     │
  │select i in yale harvard oxford ucla penn exit                        │
  │do                          # in 구문에 나열된 인자들로 메뉴가 생성됨 │
  │  case $i in                # 사용자가 선택한 항목의 번호가 $i에 설정 │
  │       yale) echo $i new england ;;                                   │
  │       harvard) echo $i new england ;;                                │
  │       oxford) echo $i england ;;                                     │
  │       ucla) echo $i west coast ;;                                    │
  │       penn) echo $i mid-atlantic ;;                                  │
  │       exit) echo So Long!                                            │
  │             exit 0 ;;                                                │
  │       *) echo not a valid choice;;                                   │
  │  esac                                                                │
  │  read ans?"continue? (y/n)"  # 사용자에 작업 계속 여부를 묻는다.     │
  │  if [ $ans = 'y' ]                                                   │
  │  then                                                                │
  │       :         <----- 다시 select 명령어에 제어가 넘어간다.         │
  │  else                        # 작업종료를 선택할 경우 이 문장이 실행 │
  │   echo education: the investment that pays back more than it costs ! │
  │   exit 0                     # select 명령문을 종료                  │
  │  fi                                                                  │
  │done                                                                  │
  └───────────────────────────────────┘

    KORN> sel     ───┐
    1) yale             │
    2) harvard          │ select 명령어에 의해 자동적으로 생성된
    3) oxford           │ 선택메뉴이다. 명령어 실행 초기에 한번만 작성된다.
    4) ucla             │ 이후는 프롬프트만이 새롭게 표시된다.
    5) penn             │
    6) exit       ───┘
    choose one: 3    <--- 사용자가 재정의한 문자열을 이용하여 표시되는
    oxford england        사용자의 선택을 요구하는 프롬프트
    continue?(y/n) y <--- read 명령어로인한 입력 요구
    choose one: 5
    penn mid-atlantic
    continue?(y/n) n <--- 작업종료 선택시의 실행 내용을 보여준다.
    education: the investment that pays back more than it costs!


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


쉘 프로그램의 제어문 : expr

1. expr 제어문

    Bourne Shell에서 모든 변수들은 문자열 type으로 처리된다.
    따라서 문자열들의 연산은 불가능하기 때문에 변수들의 연산은 불가능하다.
    그러나 Buorne Shell은 수학적 등식을 사용하여 문자열의 수학적 연산을
    수행할 수 있는 방법은 제공한다.
    이런 연산은 Bourne Shell의 expr 명령어를 이용하여 수행할 수 있다.

    expr 명령어는 수학식과 같이 그들의 인자들을 계산하고, 표준출력 장치에
    결과를 출력한다. 이 expr 명령어는 쉘 변수들의 연산을 수행하기 위해 아주
    빈번히 사용된다. expr 명령어에서 이용 가능한 연산자는 다음과 같다.

         +    더하기
         *    곱하기
         %    나머지 나누기
         -빼기
         /    나누기

    expr 명령어상에 위의 연산자들을 사용할 경우에는 연산자의 전후에
    반드시 공백을 지정해야 한다.

    expr 명령어상에서 첫번째 인자와 두번째 인자를 비교할수 있는 일치(match)
    연산자인 :도 사용할 수 있다. 그러나 이 연산자를 사용할 경우에,
    두번째 인자로는 반드시 정규 표현식을 지정해야만 한다.
    : 연산자의 연산 결과로 정규 표현식으로 지정된 문자와 일치된 문자수가
    돌려지거나 또는 일치하는 것이 없을 경우에는 0 값이 되돌려진다.
    모든 문자를의미하는 .*라는 정규 표현식을 이용하면은, 일치 연산자를
    사용하여 지정된 첫번째 인자의 인자의 길이 즉, 문자수를 확인할 수 있다.

         (주목) 역따옴표(`)로 둘러쌓인 연산식이 실행되면, 명령어의 결과가
              명령어 자체를 대치한다. 그리고 연산식내에 사용된 $ 또는 * 같은
              연산자는 expr 명령어에 처리되기 앞서 쉘에의해서 먼저 분석되기
              때문에 엉뚱한 결과를 초래할 수 있다. 이와같은 경우 사용된
              연산자를 쉘이 번역하지 못하도록 보호문자인 \를 연산자 앞에
              지정해야 한다.

    다음은 expr 명령어를 사용한 예이다.

    ┌─────────────────────────────────┐
    │ myprog                                                           │
    ├─────────────────────────────────┤
    │ a=`expr 1 + 2`        # expr 명령어의 실행 결과가 변수에 설정.   │
    │ b=`expr $a \* 4`      # *는 쉘 특수문자임으로 이를 expr 명령어의 │
    │ echo $a $b  # 인자 즉, 연산자로 전달하기 위해 \를 지정.          │
    └─────────────────────────────────┘

    $myprog
    3 12
    $VAR="Now is the time"
    $expr "$VAR" : '.*'        <----(1)
    15

    (1)번에 지정되어 있는 expr 명령어를 유심히 관찰해야 한다.
    이 예는 VAR 변수에 설장된 문자열의 문자수를 계산하기 위해
    expr 명령어를 : 연산자를 사용하여 지정하고 있다.
    그런데 변수의 참조인 첫번째 인자에 대해 쌍따옴표가 (") 지정되어 있다.
    그것은 변수 VAR에 설정되어 있는 공백을 쉘이 제거하지 못하도록 하기
    위함이다. 두번째 인자로 '.*' 라는 정규 표현식을 지정함으로써 첫번째
    인자로 지정된 변수의 문자수(공백 포함)를 계산하도록 하고 있다.

2. Korn Shell에서의 연산

    expr 명령어는 Bourne Shell에서 수행됐던 것처럼 Korn Shell에서도
    동일한 기능을 수행한다. 이전 강의에서 Korn Shell에는 문자열 type의
    변수 이외의 변수 type을 선언할수 있는 typeset 명령어에 대해 소개했다.

    이 기능말고도 Korn Shell에서는 연산을 수행할수 있는 let과 ((...))라는
    두가지의 추가 명령어를 제공한다. 이들 명령어의 형식은 다음과 같다.

              let __EXPRESSION__
              ((__EXPRESSION__))

    이들 두가지 명령어의 차이점을 설명하면 다음과 같다.

    ┌───┬────────────────────────────┐
    │      │ __EXPRESSION__상에 특정한 쉘 특수문자를 사용할 경우에는    │
    │      │ 보호문자로 묶어주어야 한다.                            │
    │let   ├────────────────────────────┤
    │      │ __EXPRESSION__상에 변수를 지정시 변수명 앞에 $를 기술할    │
    │      │ 필요가 없다.                                           │
    ├───┼────────────────────────────┤
    │      │ __EXPRESSION__상에 사용된 쉘특수문자를 보호할 필요가 없다. │
    │      │                                                        │
    │((..))├────────────────────────────┤
    │      │ __EXPRESSION__상에 사용된 변수에 $ 문자를 지정할 필요가    │
    │      │ 없다.                                                  │
    └───┴────────────────────────────┘

    명령어의 형식에서 __EXPRESSION__은 변수, 상수 또는 변수 제어문을 지정할 수
    있으며, 또한 __EXPRESSION__은 수학식, Boolean 연산식, 비트 연산식등을
    이용하여 기술할 수 있다.
    연산식에 지정 가능한 연산자들은 다음과 같다.

  ┌─────────────┬─────────────────────┐
  │ 연산자                   │  수행내용                                │
  ├─────────────┼─────────────────────┤
  │ (__EXPRESSION__)             │ Override precedence.                     │
  ├─────────────┼─────────────────────┤
  │ -__EXPRESSION__              │ 음수                                     │
  ├─────────────┼─────────────────────┤
  │ l__EXPRESSION__              │ 논리적 부정                              │
  ├─────────────┼─────────────────────┤
  │ __EXPRESSION__ * __EXPRESSION__  │ 수학적 연산                              │
  │ __EXPRESSION__ / __EXPRESSION__  │                                          │
  │ __EXPRESSION__ % __EXPRESSION__  │                                          │
  │ __EXPRESSION__ + __EXPRESSION__  │                                          │
  │ __EXPRESSION__ - __EXPRESSION__  │                                          │
  ├─────────────┼─────────────────────┤
  │ __EXPRESSION__ << __EXPRESSION__ │ 두번째 __EXPRESSION__에 지정된 비트수만큼    │
  │ __EXPRESSION__ >> __EXPRESSION__ │ 좌/우측으로 첫번째 __EXPRESSION__에 지정된   │
  │                          │ 값을 shift한다.                          │
  ├─────────────┼─────────────────────┤
  │ __EXPRESSION__ <= __EXPRESSION__ │ Boolean __EXPRESSION__s (왼쪽에서 오른쪽으로)│
  │ __EXPRESSION__ >= __EXPRESSION__ │ __EXPRESSION__의 값이 0이 아니면 1           │
  │ __EXPRESSION__ < __EXPRESSION__  │      그렇지 않으면 0이다.                │
  │ __EXPRESSION__ > __EXPRESSION__  │                                          │
  │ __EXPRESSION__ == __EXPRESSION__ │                                          │
  │ __EXPRESSION__ != __EXPRESSION__ │                                          │
  ├─────────────┼─────────────────────┤
  │ __EXPRESSION__ && __EXPRESSION__ │ 논리적 AND                               │
  │ __EXPRESSION__ || __EXPRESSION__ │ 논리적 OR                                │
  ├─────────────┴──────┬──────────────┤
  │ identifier = __EXPRESSION__                │ 할당 연산자                │
  │ identifier = identifier op __EXPRESSION__  │                            │
  └────────────────────┴──────────────┘

    다음은 let과 ((..)) 명령어의 예제는 다음과 같다.

    KORN> let x=10*3    # 10 * 3 즉, 30이 변수 x에 설정된다.
    KORN> echo $x
    30
    KORN> ((z=x/2))     # 30 / 2 즉, 15가 z에 설정된다.
    KORN> echo $z
    15
    KORN> let z=x/2     # 30 / 2 즉, 15가 변수 z에 설정된다.
    KORN> echo $z
    15
    KORN> let y=x\ echo $y
    1
    KORN> if (($x < z)) # x가 z보다 적으면 then 문을 그렇지 않으면 else문을
    >then               # 실행한다.
    >   echo x \> z
    >   echo $x \< $z
    >else
    >   echo x is not \< z
    >   echo $x \> $z
    >fi
    x is not < z
    30 > 15


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



쉘 프로그램의 제어문 : while

1. while loop

    while loop는 명령어의 종료상태를 기준으로 작업을 제어한다는 점에서
    유사하다. 그러나 if는 종료상태에 따라 지정된 명령어를 한번만수행하는
    것과 달리 while loop는 종료상태가 거짓(false)이 될때까지 루프안에
    지정된 명령어의 실행을 계속한다는 차이가 있다.

    while 명령어의 형식은 다음과 같다.

              while cmd_list
              do
                   command1
                   command2
                   ...
                   commandN
              done

    위 명령어의 형식에서 cmd_list는 루프의 수행 여부를 결정하기 위해
    검사되어야 할 명령어를 지정하는 부분이다. cmd_list 부분에는
    복수개의 명령어를 지정할 수 있는데, 만일 복수개의 명령어를
    지정했을 경우 루프의 수행 여부는 cmd_list에 지정된 마지막 명령어의
    종료상태로 판단된다. 명령어의 종료상태가 참일 경우에는 do와 done 사이에
    지정되어 있는 명령어들을 실행한다. 거짓일 경우에는 do와 done사이에
    지정된 명령어를 실행하지 않고 while 루프를 종료한다.

    다음은 while 루프의 사용예를 보여준다.

  ┌──────────────────────────────────┐
  │ myprog                                                             │
  ├──────────────────────────────────┤
  │ while test -f junkfile        # junkfile이 삭제될 경우에 거짓이    │
  │ do                            # 되어 while 구문이 종료.            │
  │     echo junkfile still on disk                                    │
  │     sleep 5                   # 5초간 실행을 중지한다.             │
  │ done                                                               │
  └──────────────────────────────────┘

    $ls j*
    junkfile
    $myprog
    junkfile still on disk        # sleep 명령어로 인해 5초 간격으로 표시.
    junkfile still on disk
    junkfile still on disk
    ...
                          # junkfile이라는 화일이 삭제되어야만
                                  # 프로그램이 종료한다. 지금은 foreground
                                  # 에서 실행되고 있기 때문에 화일을 삭제
                                  # 할 수가 없어서 사용자가 로
                                  # 프로그램 실행을 실행중단시켰다.
    $myprog&                      # 이번엔 프로그램을 background에서 실행..
    284
    $junkfile still on disk       # background에서의 실행결과 표시되는 출력
    junkfile still on disk
    rm junkfile                   # 사용자가 rm 명령어를 이용, 화일을 삭제.
    $                             # 프로그램이 종료되었음을 확인할 수 있다.


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



쉘 프로그램의 제어문 : until
--------------------------------------------------------------------

1. until loop

    until loop는 while loop와 반대 형식의 작업을 수행한다.
    while loop는 명령어의 종료상태가 거짓(false)이 될때까지 루프안에
    지정된 명령어의 실행을 계속하는데 반해, until loop는 명령어의
    종료상태가 참(true)이 될때까지 지정된 명령어를 실행한다.

    until 명령어의 형식은 다음과 같다.

              until cmd_list
              do
                   command1
                   command2
                   ...
                   commandN
              done

    위 명령어의 형식에서 cmd_list는 루프의 수행 여부를 결정하기 위해
    검사되어야 할 명령어를 지정하는 부분이다. cmd_list 부분에는
    복수개의 명령어를 지정할 수 있는데, 이경우 루프의 수행 여부는
    cmd_list에 지정된 마지막 명령어의 종료상태로 판단된다.
    명령어의 종료상태가 거짓일 경우에는 do와 done 사이에
    지정되어 있는 명령어들이 실행되나, 참일 경우에는 do와 done사이에
    지정된 명령어를 실행하지 않고 until 루프를 종료한다.

    다음은 until 루프의 사용예를 보여준다.

  ┌──────────────────────────────────┐
  │ myprog                                                             │
  ├──────────────────────────────────┤
  │ until test -f junkfile     # junkfile이라는 화일이 존재할 경우에   │
  │ do                         # 참이되어 until을 종료한다.            │
  │     echo junkfile still on disk                                    │
  │     sleep 5                # 5초간 실행을 중지한다.                │
  │ done                                                               │
  └──────────────────────────────────┘

    $ls j*
    j*: No such file or directory # 화일이 없는것을 확인할 수 있다.
    $myprog
    junkfile still on disk        # sleep 명령어로 인해 5초 간격으로 표시.
    junkfile still on disk
    junkfile still on disk
    ...
                          # junkfile이라는 화일이 존재해야만
                                  # 프로그램이 종료한다. 지금은 foreground
                                  # 에서 실행되고 있기 때문에 화일을 생성
                                  # 할 수가 없어서 사용자가 로
                                  # 프로그램 실행을 실행중단시켰다.
    $myprog&                      # 이번엔 프로그램을 background에서 실행..
    284
    $junkfile still on disk       # background에서의 실행결과 표시되는 출력
    junkfile still on disk
    cat > junkfile                # 사용자가 cat 명령어를 이용 화일을 생성.
    
    $                             # 프로그램이 종료되었음을 확인할 수 있다.


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



쉘 프로그램의 제어문 : while과 until

    while과 until 명령어에서는
    cmd_list상에 지정되어 있는 마지막 명령어의 종료상태가
    while인 경우 참일때까지, until은 거짓일때까지
    do와 done 문장사이의 명령어들이 매번 루프가 수행될때 마다 실행된다.
    그리고 cmd_list내의 모든 명령어들도 매번 루프의 처음에서
    실행되어진다. 단, cmd_list상에 복수개의 명령어가 지정되어
    있을 경우 루프 수행여부의 판단은 마지막 명령어의 종료상태로 결정된다.

    다음은 이를 확인하기 위한 예제이다.

    ┌────────────────┐
    │      Bourne/Korn               │< 이 예는 Bourne/Korn Shell 모두에서
    ├────────────────┤  실행 가능한 프로그램이다.
    │$x=0                            │
    │$while echo testing             │< while 구문상의 cmd_list 부분에는
    │>    echo here $x               │  3개의 명령어가 지정되어 있다.
    │>    test $x -ne 3              │  이 경우 while의 조건 판단의 기준은
    │>do                             │  가장 마지막에 지정된 명령이 된다.
    │>    x=`expr $x + 1`            │< 조건이 참일 경우에만 실행되는
    │>    echo inside $x             │  명령어들이다.
    │>done                           │
    │testing                         │
    │here 0                          │
    │     inside 1                   │
    │testing                         │
    │here 1                          │
    │     inside 2                   │
    │testing                         │
    │here 2                          │
    │     inside 3                   │
    │testing                         │
    │here 3                          │< x 변수의 값이 3임으로 거짓이되어
    └────────────────┘  프로그램이 종료된다.

    ┌────────────────┐
    │             Korn               │< Korn Shell 환경에서만 수행가능하다.
    ├────────────────┤  왜냐하면 (( 연산자는 Korn Shell
    │KORN>x=0                        │  에서만 지원하기 때문이다.
    │KORN>until echo testing         │< 조건이 참이될때까지 지정한 명령어들
    │>    echo here $x               │  을 반복 수행한다.
    │>    test $x -eq 3              │
    │>do                             │
    │>    ((x=x+1))                  │< (( 연산자는 Korn Shell 고유의
    │>    echo inside $x             │  Math 연산자이다.
    │>done                           │
    │testing                         │
    │here 0                          │
    │     inside 1                   │
    │testing                         │
    │here 1                          │
    │     inside 2                   │
    │testing                         │
    │here 2                          │
    │     inside 3                   │
    │testing                         │
    │here 3                          │
    └────────────────┘


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



쉘 프로그램의 제어문 : true와 false

지금까지 소개한 몇개의 제어문에서는, 제어문상에 지정된 명령어의
실행 결과에 따라서 참과 거짓에 따른 조건 분기를 실행하였다.

그런데 프로그램 작성시 반복 제어문들을 사용하고자 할 경우에,
한가지 제약이 따르게 된다. 그것은 반복 제어문상에 반드시 반복 조건을
결정하기 위한 명령어를 지정해야 한다는 것이다.
예를들어 while 구문을 살펴보자.

while구문은 반드시 다음과 같은 형식을 가져야만 한다.

         while 조건_판단_명령어
         do
           반복실행할_명령문
         done

이와 같은 경우 조건_판단_명령어를 반드시 지정해야 하는가?
그렇다, while 구문에서는 반드시 조건_판단_명령어를 지정해야만 한다.
그런데 프로그램 작성시 반복적으로 명령어들을 실행하기 위해, while 구문은
사용해야만 하는데, 마땅히 지정할 조건_판단_제어문이 없을 경우가 있다고 하자.
이 경우 while 구문을 사용하지 못하게 될것이다.
바로 이런 경우에 요긴하게 사용할 수 있도록,
Shell에서는 true와 false라는 두개의 제어문을 추가로 제공한다.

true 제어문은 항상 true(참)이라는 값을 돌려주며, false 제어문은 항상
false(거짓)이라는 값을 돌려준다.

다음은 if 제어문과 함께 true와 false 명령어를 사용하는 예제이다.

  ┌───────────────┐
  │$if true                      │< true 명령어로 인해, if 제어문의
  │>then                         │  조건 판단 결과가 항상 참(true)가 된다.
  │>   date                      │
  │>fi                           │
  │Tue Oct 23 11:20:31 EDT 1990  │
  │                              │
  └───────────────┘
  ┌───────────────┐
  │$if false                     │< false 명령어로 인해, if 제어문의
  │>then                         │  조건 판단 결과가 항상 거짓(false)가
  │>   date                      │  된다.
  │>fi                           │
  │$                             │
  └───────────────┘

위의 예제에서의 true/false 제어문은 모순없이 사용되고 있다.
그런데 다음과 같은 경우에는 모순이 발생한다.

  ┌───────┐
  │while true    │ < while 구문은 조건이 참일 경우에는 계속적으로
  │do            │   지정한 명령어(여기서는 ls)를 반복적으로 수행한다.
  │     ls       │   그래서 이 예의 경우 무한루프를 돌것이다.
  │done          │
  └───────┘

이와같은 경우에는 while 제어문을 종료하는 싯점을 do와 done사이에서
선언해 주어야 한다. 바로 이와같은 경우를 위해 Shell에서 break및 continue
명령어를 제공한다.

다음 강의에서 break 및 continue 명령어에 대해 자세히 소개한다.


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


쉘 프로그램의 제어문 : break와 continue

이전 강의에서 while 구문상에 true 또는 false 명령어를 사용했을 경우,
무한 루프가 수행될 가능성이 있음을 설명했다. 그리고 이런 경우
반복 수행되는 명령어상에 루프를 종료하기 위한 명령어를 지정해야 한다.
이런 경우 이전에 설명했던 exit 명령어를 지정한다면 어떻게 될까?
exit 명령어는 쉘 프로그램 자체를 종료하는 명령어이기 때문에
프로그램 자체가 종료하게된다. 그렇기때문에 쉘 프로그램에서
루프 제어문 이후에 기술된 명령어들은 실행되지 않게된다. 바로 이와같은
경우, 쉘 프로그램은 종료하지 않고, 루프만을 종료할 수 있도록 제공되는
명령이 바로 break 명령어이다.

1. break 명령어

break 명령어는 루프 제어문에서 유용하게 사용된다. break명령어는
루프 제어문 이후에 지정된 명령어들의 실행을 계속하기 위하여, 루프를
종료할 수 있게 해준다. break 명령어는 모든 루프 제어문들에서
사용 가능하다. break 명령어의 형식은 다음과 같다.

         break [n]

위 형식에서 n은 종료할 루프의 갯수를 나타내는 인자로서, 필요시 지정한다.
예를들면, 하나의 루프 제어문내에 또하나의 루프문이 지정되어 있을 경우,
1을 지정하면 안쪽 루프만을 종료한 후 바깥쪽 루프를 계속하여 수행한다.
그러나 n으로 2를 지정할 경우에는 안쪽및 바깥쪽 루프 모두를 함께종료한후,
이후 지정된 명령어들을 수행한다.

다음은 break 명령어의 사용예이다.

    ┌────────────────┐
    │while.ex                        │
    ├────────────────┤
    │while true                      │ 이 예에 지정된 command들이 수행되는
    │do                              │ 순서를 살펴보자.
    │   command1                     │ 1) junkfile이 존재하고 있는 경우
    │   if [-f junkfile]             │    command1
    │   then                         │    rm junkfile
    │       rm junkfile              │    command2
    │   else                         │    command1
    │       break                    │    break
    │   fi                           │    command3
    │   command2                     │2)junkfile이 기존에 없었던 경우
    │done                            │    command1
    │command3                        │    break
    └────────────────┘    command3

위에서 설명한내용을 잘살펴보면, break 명령어의 사용예를 잘 이해할수 있을
것이다. 독자 스스로 깊이 생각해보기를 바란다.

2. break의 명령어의 대체형식

비록 break 명령어가 루프의 외부로 빠져나올수 있다 해도, 많은 프로그래머들은
루프의 외부로 빠져나오는데 있어서 빈약한 프로그래밍 형식으로 여긴다.
구조적 프로그래밍 방식으로 프로그램 작성시에는, 루프에 하나의 입구 및 하나의
출구가 선언되어야 하는데, break 명령어를 이용한 프로그램에서는 이와같은
프로그램 작성이 곤란하다. 이와 같은 경우 루프조건을 변경함에 의해서 break
명령어를 사용하지 않고도, 루프를 벗어나도록 프로그램을 작성할 수 있다.

다음은 break 명령어를 사용하지 않고, 루프를 벗어나는 방식에 대한 예이다.

┌──────────────┐
│break.alt                   │
├──────────────┤
│command1                    │
│while [-f junkfile]         │< while 루프는 junkfile이라는 화일이
│ do                         │  존재하지 않을때까지 반복 수행된다.
│   rm junkfile              │< 루프 반복문상에 지정되어 있는 rm 명령어에
│   command2                 │  의해, while 조건이 거짓이되어 루프를
│   command1                 │  벗어나게 한다. 즉, rm 명령어가 break
│ done                       │  명령어의 대체 형식으로 사용되고 있다.
│command3                    │
└──────────────┘

3. continue 명령어

continue 명령어는 루프상에 지정되어 있는 후속된 명령어를 수행하지 않고,
루프의 시작부분으로 제어를 옮기기 위해서 사용하는 루프 제어문과 함께
빈번하게 사용하는 제어문이다.

continue 명령어의 형식은 다음과 같다.

         continue [n]

여기에서 n은 계속 수행할 루프를 지정하기 위해, 필요시 사용하는 인자이다.
예를들어 하나의 루프안에 또하나의 내부 루프 그리고 그안에 또 하나의 루프가
선언되어 있다고 했을때, 가장 내부 루프안에 continue 2라고 지정되어 있으면,
작업은 가장 외부의 루프로부터 계속 수행된다. 인자없이 continue 문을 수행했을
경우에는 1이 가정된다.


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



Shell Programming의 추가사항 : signal
--------------------------------------------------------------------

시그널은 비동기적인 사건들의 발생을 프로세스에게 알려주기 위한 수단으로
사용된다. 프로세스들은 시스템 호출(대표적으로 kill)을 통해 서로간에
시그널을 보낼 수도 있고, 또는 커널이 내부적으로 시그널을 보낼 수도 있다.
SVR 4.0 유닉스는 프로세스에 보낼수 있는 아래 도표와 같은 31개의 시그널을
제공한다.

다음은 SVR 4.0 유닉스에서 사용하는 시그널들을 정리한 것이다.

┌───┬────┬───────────────────────┬───┐
│ 번호 │ 이 름  │ 용  도                                       │Action│
├───┼────┼───────────────────────┼───┤
│   0  │ EXIT   │Exit from the shell                           │Exit  │
├───┼────┼───────────────────────┼───┤
│   1  │ HUP    │Hang-up                                       │Exit  │
├───┼────┼───────────────────────┼───┤
│   2  │ INT    │Interrupt(<Delete> or )               │Exit  │
├───┼────┼───────────────────────┼───┤
│   3  │ QUIT   │Quit(-l;core dump)(ASCII FS)         │Core  │
├───┼────┼───────────────────────┼───┤
│   4  │ ILL    │Illegal instruction(not reset when caught)    │Core  │
├───┼────┼───────────────────────┼───┤
│   5  │ TRAP   │Trace trap(not reset when caught)             │Core  │
├───┼────┼───────────────────────┼───┤
│   6  │ IOT    │Used by Abort                                 │Core  │
├───┼────┼───────────────────────┼───┤
│   7  │ EMT    │Emulation Trap                                │Core  │
├───┼────┼───────────────────────┼───┤
│   8  │ FPE    │Floating point exception                      │Core  │
├───┼────┼───────────────────────┼───┤
│   9  │ KILL   │Kill(cannot be caught or ignored)             │Core  │
├───┼────┼───────────────────────┼───┤
│  10  │ BUS    │Bus error                                     │Core  │
├───┼────┼───────────────────────┼───┤
│  11  │ SEGV   │Segmentation violation                        │Core  │
├───┼────┼───────────────────────┼───┤
│  12  │ SYS    │Bad argument to a system call                 │Core  │
├───┼────┼───────────────────────┼───┤
│  13  │ PIPE   │Write to pipe without a process to read it    │Exit  │
├───┼────┼───────────────────────┼───┤
│  14  │ ALRM   │Alarm time-out                                │Exit  │
├───┼────┼───────────────────────┴───┤
│  15  │ TERM   │Software termination signal(sent by kill by default   │
│      │        │                                              │Exit  │
├───┼────┼───────────────────────┼───┤
│  16  │ USR1   │User defined signal 1                         │Exit  │
├───┼────┼───────────────────────┼───┤
│  17  │ USR2   │User defined signal 2                         │Exit  │
├───┼────┼───────────────────────┼───┤
│  18  │ CHLD   │Death of child                                │Ignore│
├───┼────┼───────────────────────┼───┤
│  19  │ PWR    │Power-fail reset                              │Ignore│
├───┼────┼───────────────────────┼───┤
│  20  │ WINCH  │Window size change                            │Ignore│
├───┼────┼───────────────────────┼───┤
│  21  │ URG    │Urgent socket condition                       │Ignore│
├───┼────┼───────────────────────┼───┤
│  22  │ IO     │Socket I/O possible                           │Ignore│
├───┼────┼───────────────────────┼───┤
│  23  │ STOP   │Stopped signal (cannot be caught or ignored)  │Stop  │
├───┼────┼───────────────────────┼───┤
│  24  │ STP    │Stopped user                                  │Stop  │
├───┼────┼───────────────────────┼───┤
│  25  │ CONT   │Continued                                     │Ignore│
├───┼────┼───────────────────────┼───┤
│  26  │ TTIN   │Stopped tty input                             │Stop  │
├───┼────┼───────────────────────┼───┤
│  27  │ TTOU   │Stopped tty output                            │Stop  │
├───┼────┼───────────────────────┼───┤
│  28  │ ALRM   │Virtual timer expired                         │Exit  │
├───┼────┼───────────────────────┼───┤
│  29  │ PROF   │Profiling timer expired                       │Exit  │
├───┼────┼───────────────────────┼───┤
│  30  │ XCPU   │CPU time limit exceeded                       │Core  │
├───┼────┼───────────────────────┼───┤
│  31  │ XFSZ   │File size limit exceeded                      │Core  │
└───┴────┴───────────────────────┴───┘

수행중인 프로세스가 시그널을 받았을 경우, 그 시그널에 상응하는
동작(Action)이 수행되는데, 대략적으로 시그널에 의해 수행되는 동작으로는
프로세스의 종료(exit), 시그널의 무시(Ignore) 그리고 특정의 사용자및 시스템
함수(Function)의 실행등이 있다. 시그널별로 각각 수행되는 동작은 시그널을
발생하는 프로세스별로 지정되어 있다.

그리고 위의 31가지 시그널을 그 용도에 따라서 분류하면 다음과 같이
분류할 수 있다.

    1) 한 프로세스의 종료와 연관된 시그널들
    2) 프로세스에 의해 유발된 예외(exception)와 연관된 시그널들
    3) 시스템 호출 수행중의 복구할 수 없는 상태와 관련된 시그널들
    4) 시스템 호출 수행중 예기치 않는 에러상태에 의한 시그널들
    5) 사용자 모드의 프로세스가 유발하는 시그널들
    6) 터미널의 작업과 관련된 시그널등
    7) 한 프로세스의 실행을 추적하기 위한 시그널등

각각의 시그널은 시스템에 특별한 의미를 갖고 있는데, 통상 시그널은 에러 발생
조건들과 관련된다.

위의 도표에 나와있는 시그널들은 번호 또는 이름중 하나를 통해 참조할 수 있다.
가장 일반적으로 사용자가 사용하는 시그널 제어 명령은 kill 명령어인데,
kill 명령어 사용시, 사용자는 kill 명령어에 의해 발생시킬 시그널을 인자로
지정해야 한다. 이때 시그널을 지정하는 방식은 두가지가 있는데, 그 형식은
다음과 같다.

    1) kill -signal_name PID
    2) kill -signal_number PID

사용자가 작성한 쉘 프로그램이 실행 요구되면, 하나의 프로세스로서
시스템에 의해 실행된다. 그렇기때문에 다른 프로세스로부터  시그널이
전달될 수도 있을 것이다. 이런경우 각각의 시그널들에 정의되어 있는
동작(예를들어 종료등)들이 수행된다.

그런데 예를 하나 들어보자. 사용자가 쉘 프로그램에서 여러 화일을 생성하여,
그 화일에 데이타들을 저장하는 작업을 하고 있다고 하자. 그런데 이 작업이
미처 끝나기도 전에 종료 시그널이 발생하면, 이 프로그램의 실행후에는
여러가지 쓰레기처럼 필요없는 화일들이 생길수 있을 것이다. 이런 경우를
대비하기 위해 사용자는 특정 시그널이 전달되었을때, 시그널에 정의되어 있는
동작을 취하기 앞서서, 수행할 작업들을 trap 명령어를 이용하여 선언할 수 있다.
이들 trap 명령어에 대해서는 다음 강의에서 자세히 소개한다.


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



Shell Programming의 추가사항 : trap
--------------------------------------------------------------------

이전 강의에서 trap 명령어는 쉘 프로그램이 프로세스로써 실행도중,
다른 프로세스에 의해 전달된 특정 시그널에 대한 사용자 지정 동작을
선언하기 위해사용한다고 설명했다. 이번 강의에서는 trap 명령어에 대한
활용 방식에 대해 설명하기로 한다.

1. trap 명령어

trap 명령어는 프로세스가 시그널을 전달받았을때, 그 시그널에 정의되어
있는 동작을 수행하기 앞서서(또는 대신해서), 사용자가 지정하는 특정 동작을
수행하도록 할수 있는 기능을 제공하는 명령어이다.

trap 명령어에는 다음과 같은 3가지 사용 형식이 있다.

    1)   ┌─────────────────────────┐
         │ trap "command1; command2; ...; commandN" signal  │
         └─────────────────────────┘

         -. 지정한 시그널이 전달되었을 경우, 그 시그널의 디폴트 동작을
            수행하기 앞서서(또는 대신해서) , " "속에 나열되어 있는 명령어들을
            순차적으로 수행한다.

    2)   ┌─────────┐
         │ trap '' signal   │
         └─────────┘

         -. 지정한 시그널을 무시한다.

    3)   ┌───────┐
         │ trap signal  │
         └───────┘

         -. 지정한 시그널에 대한 trap을 해제한다.

통상의 trap 명령어의 사용 용도는 다음과 같다.

    1) 작업을 끝내기 전에 프로그램의 수행도중 생성했던 일시적 화일들을
       제거하기 위해서

    2) 프로세스의 가로채기를 피하기 위해 쉘로 보내진 시그널을 무시하기
       위해서

    (주의) trap 명령어내에 변수가 지정되어 있는 경우, 변수의 올바른 대치가
           수행되도록 하기 위해서는 '(따옴표)로 묶어 주어야 한다.
           예를들어 EXIT 시그널이 전달 되었을때, 사용자의 홈 디렉토리속에
      있는 logout 스크립트를 수행하도록 하기 위해서는 다음과
           같이 지정한다.
                             trap '$HOME/logout' EXIT

다음은 trap 명령어의 사용예이다.

┌───────────────────────────────┐
│ trapex                                                       │
├───────────────────────────────┤
│ trap "rm -f junkfile;echo good-bye world;exit" 1 2 15        │
│ while test -f junkfile                                       │
│ do                                                           │
│   echo junkfile still on disk                                │
│   sleep 5                                                    │
│ done                                                         │
└───────────────────────────────┘

    $trapes
    junkfile still on disk        < 프로그램의 while 루프에 지정되어 있는
    junkfile still on disk          echo 명령에 의한 실행 결과. sleep
    junkfile still on disk          명령어로 인해 5초마다 한번씩 표시된다.
    ...
    <DELETE>                      < 사용자가 <DELETE>키를 입력하면,EXIT
    good-bye world                  시그널이 전달되는데, 이때 trap 명령어로
                                    인해서 junkfile이 삭제된후,
                                    good-bye world라는 메세지가 표시된다.

    $ls junkfile                  < 프로그램 실행후 실제 junkfile의 존재여부를
    junkfile:No such file or directory      확인하고 있다.
</pre>