쉘(shell) 개요


[1] shell 개요

[2] shell 프로시저와 shell 스크립트

[3] 각 shell의 관계

[4] 주석문과 파이프

[5] 요약


shell은 사용자와 유닉스의 핵심인 커널을 연결시켜주는 중간적인 역할을 한다. 뿐만 아니라, 외부의 영향으로부터 시스템 하드웨어를 보호 하기도 한다.

여기에서는 이러한 shell에 대하여 그 기능과 역할에 대하여 알아본다. 그리고, shell의 프로그래밍 환경이 어떻게 이루어 지는지도 알아보겠다.

shell 프로그래밍의 작성과 이들의 실행 하기까지의 과정도 알아볼 것이다. 간단한 스크립트를 vi 등의 에디터를 사용하여 작성을 한 후 이 스크립트에 실행 허가를 주어서, 실행을 해보고 결과를 확인해 본다.

그리고, 마지막으로는 shell 스크립트를 하는데 있어서 반드시 필요한 파이프(pipe)의 개념과 사용 등에 대한 내용도 같이 알아보기로 한다.






[1] shell 개요


1-1. 소개


< 인간 사회에서 통용이 되는 언어와 기계가 사용하는 언어에는 많은 차이가 있다. 인간은 자신들이 사용하는 언어 그대로를 기계에서 사용하고 싶어 한다. 이러한 경우, 인간이 사용하는 언어를 기계가 이해할 수 있는 언어로 해석하여, 그 의미를 기계에 전달하여 기계가 해당 의미대로 수행 되게끔 한다. 이와 같이 UNIX 시스템에서 중간 인터페이스 역할을 해주는 것을 shell() 이라 한다. 즉, 시스템 운영 체제인 유닉스와 사용자가 서로 의사 소통을 할 수 있도록 도와 주는 프로그램이라 할 수 있다.

UNIX는 커널(kernel), shell(shell), 사용자(User 또는 애플리케이션(application))이라는 3가지 구조를 갖는다. 다음 [그림1]은 UNIX의 구조를 간략히 보여주고 있다

UNIX 구조
[그림1] UNIX 구조


[그림 1]을 보면 실질적인 하드웨어를 제어하고 직접 접근하는 것은 커널(KERNEL)이라는 부분이다. 또 이 커널에 접근이 되는 부분이 바로 shell 이라는 부분이다. 그리고, 실질적인 프로그램이나 사용자들은 이 shell에 접근이 되어진다. 이것은 사용자의 명령어나 프로그램 등이 바로 기계의 하드웨어에 전달되는 것이 아니라, shell에 의해 그 의미와 내용이 기계어로 해석이 되어 커널에 전달이 되면 커널은 이를 하드웨어를 조작하는 운영을 하게 된다. 그래서, shell을 커널과 사용자 사이의 인터페이스(Interface) 역할을 한다고 표현한다.

위 그림에서 커널은 UNIX의 가장 중요한 부분으로서 UNIX의 심장부라 할 수 있고, 애플리 케이션은 사용자가 수행하는 프로그램이 된다. 가운데 있는 shell은 커널과 애플리 케이션의 교량적인 역할을 한다. 해서 shell을 정의한다면 '사용자와 순수 운영체제 사이를 연결시켜주는 기능을 수행하는 중간적인 존재이다.' 라고 할 수 있다.

유닉스의 핵심 부분인 커널은 시스템이 부팅되어 종료 될 때 까지 메모리에 상주를 하게 된다. 이 커널은 프로세스들의 생성과 제어를 담당하고, 메모리나 파일 시스템, 애플리케이션 등의 관리를 담당한다. Shell은 프로그램이며, 다른 프로그램들도 모두 디스크에 존재한다. 커널은 이들 프로그램들을 메모리로 로드시켜 실행하고 이들이 종료를 하게 되면 이들이 사용한 재원들을 다시 정리하여 재 사용이 가능하게끔 운영을 한다. shell은 원래 디스크에 존재하는 프로그램이다. 사용자가 시스템에 로그인을 하게 될 경우 커널에 의해서 메모리에 로드(load)되어 시작을 하는 하나의 유틸리트이다.


다음은 shell은 다음의 기능을 수행한다.

    ▶ 사용자와 커널사이의 인터페이스(interface) 역할

    ▶ 사용자가 입력한 명령어를 읽고 해석하는 프로그램 (명령어 해석기 : command processor)

    ▶ 해석형 프로그래밍 언어
     - shell 스크립트(script 또는 프로시져(prodecure))라는 shell 명령과 문장으로 이루어진
       파일 작성 가능

    ▶ 사용자는 자신의 shell 스크립트(script)를 생성할 수 있고, 이 크립트(script)의 실행은
      사용자가 표준 UNIX 명령어를 입력하는 것과 같은 방법으로 수행


이러한 shell의 기능들은 UNIX 시스템을 더 강력하고 유연하게 해준다. shell은 사용자가 커널에게 또는 커널이 사용자와 어떠한 정보를 주고 받을 때 그 중간에서 교량역할을 한다. 그 중에는 사용자가 입력한 명령어를 커널이 수행 가능하도록 해석하기도 한다. 이로 인해 shell을 명령어 해석기(command processor)라고 부른다. DOS에서의 command.com과 같은 역할이다. 그리고, shell은 해석이 가능한 프로그래밍 구현이 가능하다. 이를 스크립트(script) 라고 하며, 관련 프로그램을 사용자가 직접 작성하여 수행을 할 수 있다. 내용은 일반적으로 사용되는 UNIX 명령어와 shell 프로그래밍 언어 등으로 구성된다.

shell을 잘 다루다는 것은 그만큼 shell에 대하여 풍부한 경험과 지식을 가지고 있음을 의미한다. 이것은 UNIX 시스템의 효율적인 운영의 밑바탕이 되기도 한다.

shell을 사전적 의미로 보면 보통 '조개 껍질' 등으로 해석이 된다. 조개 껍질은 내부를 보호하고, 경우에 따라서는 먹이를 흡수하기 위해 약간 벌리기도 한다. 이처럼 조개 껍질은 조개 내부를 외부로부터 보호해 주고, 경우에 따라서는 외부와 연결을 해주는 기능을 한다. 와 같이 shell은 UNIX내부를 보호하기도 하면서 사용자와 UNIX내부를 연결시켜 주는 역할을 수행한다.

앞으로 자세히 설명이 되겠지만 shell은 Bourne shell, Korn shell, C shell을 가장 많이 사용하고 있으며, 여기에서 보여지는 모든 예제는 Solaris 8 환경에서 직접 수행 한 결과이며, shell의 선택은 사용자의 기호나 능력과 그리고 호환성 등의 문제일 경우가 많다.

shell은 UNIX의 일부분이다. 사용자가 시스템을 사용하기 위해 접근(=로그인)이 이루어 지면 shell은 활성화 되고, 이 사용자가 작업을 끝내고 종료 하면 shell도 동작을 멈추게 되어 shell도 종료가 된다. 즉, telnet 등으로 시스템에 로그인하면 shell이 활성화 되어 사용자 인터페이스 역할을 하다가 사용자가 작업이 끝나고 telnet 접속을 종료하면 shell도 종료가 된다.


다음은 하나의 명령어가 입력 되었을 때, shell에 의해서 실행이 되는 순서이다.
  1. 사용자의 입력을 기다린다(프롬프트를 출력)

  2. 명령어 라인에서 명령어를 입력
    - 프롬프트에서 입력한 내용을 워드(word 이를 토큰(toekn)이라 한다) 단위로 해체
    - 워드는 스페이스와 탭으로 구분 되어진 것들을 말하고, 명령줄은 새로운 줄 문자로 끝이 난다.

  3. 명령어를 해석하여 처리
    - 첫번째 워드가 내장 명령어인지 아니면 디스크에 위치하는 실행 프로그램인지를 검사
    - 내장 명령어이면 내부적으로 실행
    - 실행 프로그램이면 shell은 경로 변수(PATH)에 지정된 디렉토리에서 해당 프로그램을 검색하여, 프로그램을 찾으면 shell 은 새로운 프로세스를 할당해서 그 프로그램을 실행
    - 프로그램이 실행되는 동안 shell은 대기 상태(가끔 프로그램의 진행 상황에 대한 보고를 한다)

  4. 명령어 실행 후 다시 사용자의 입력을 기다린다.
    - 프로그램 실행dl 완료 되면 프롬프트가 다시 나타나 새로운 명령어 입력 대기상태

shell은 어떤 명령어를 처리할 경우 항상 위의 순서를 기본 구조로 하여 실행이 된다.

위 과정에서 명령어를 해석할 경우 다음의 순서대로 해석을 한다.
  (1) 알리아스(alias)
  (2) 키워드(Keyword)
  (3) 함수(bash)
  (4) 내장 코맨드(Internal command)
  (5) 실행 프로그램(run program)



1-2. 로그인 쉘


로그인 shell은 사용자가 로그인을 하면서 가지는 사용자 shell이라 할 수 있다. 즉, 로그인 shell은 사용자(user)가 시스템에 로그인 하였을 때 자동으로 시작 되며, 사용자는 자신의 사용자 계정이 시스템에 처음 생성이 될 때 사용 하고픈 shell을 설정하게끔 시스템 관리자에게 요청하여 설정한다. 또는 사용 중에 자신이 사용하고픈 shell이 있다면 간단하게 전환하는 방법도 있다.

사용자의 로그인 shell은 /etc/passwd 파일에서 마지막 필드(field)에 정의 된다. 이 파일은 시스템 관리자에 의해서만 접근이 가능하기 때문에 자신이 사용하고픈 shell은 시스템 관리자에게 신청을 해야 한다. 다음은 /etc/passwd 파일의 예를 보여 주고 있다.

   # cat /etc/passwd
   root:x:0:1:Super-User:/:/bin/csh
   :
   lsoo:x:1001:1000: lsoo's home directory:/home1/kim:/bin/csh
   hong:x:1002:1000:hong's home directory:/home1/hong:/bin/ksh

이 시스템의 root라는 계정은 C shell을 사용하고 있고, lsoo이라는 사용자는 C shell, hong 이라는 사용자는 Korn shell을 사용 하게끔 설정되어 있다.

다음은 /etc/passwd 파일의 필드에 대해 정리 한 것이다.

/etc/passwd 파일의 필드 구분
[그림2] /etc/passwd 파일의 필드 구분


위와 같이 /etc/passwd 파일은 7개 필드(field)로 구분이 되는데 구분의 기준은 ': (콜론)'을 사용하고, shell은 이 파일의 7번째 필드에 설정이 된다.

lsoo이라는 사용자 계정은 C shell을 사용하도록 설정이 되어 있는데, 다른 shell을 기본 shell로 설정 하여 사용하고자 한다면 shell 필드를 다음 표에 맞게 설정 해주면 사용자의 기본 shell은 변경이 된다.

[표 1]에 있는 내용은 가장 많이 사용되는 shell들이며, Solaris에서는 기본적으로 다음 4가지만 지원한다.
[표1] shell 경로 지정
shell실행 파일
C shell /bin/csh
Korn shell /bin/ksh
Bourne shell(UNIX의 표준 shell) /bin/sh
Job shell /bin/jsh


기본 shell이 설정이 되었다면 사용자는 자신이 어떠한 shell을 사용하고 있는지 확인할 필요가 있다. 이는 shell에 따라 수행하는 명령어가 차이가 있고, 환경도 다르기 때문이다. 이 경우 자신의 로그인 shell을 확인 하기 위해서 다음 2가지 방법 중 어는 하나만을 사용한다.

   (1)# echo $SHELL

   (2)# ps
     # ps
       PID TTY    TIME CMD
       5727 pts/7    0:00 csh

     # echo  $SHELL
     /bin/csh
lsoo라는 계정은 /bin/csh, 즉 C shell을 사용하고 있다.


1-3. 로그인에서 로그 아웃까지


각 사용자가 로그인 하여 로그 아웃 하는 과정을 간략히 보면 다음과 같다. 시스템이 가동이 되면 커널은 항상 메모리에 상주 하여 시스템이 종료 하기 전까지는 살아있다.

부팅을 하는 과정에 init이라는 프로그램이 동작을 한다. 이 프로그램은 항상 백그라운드로 동작을 하는데, 시스템이 종료되기 전까지 활동을 한다. 이 프로그램은 사용자가 접속의 요구가 있거나 로그인 을 위해 다른 시스템에서의 접근이 이루어 지면 init 프로그램은 getty 프로그램을 호출(call)하여 접속이 된 터미널에 "login: "이라는 것을 출력한다. 이것으로 시스템과 대화를 할 수 있는 준비가 끝난 것이다.

로그인 ID를 입력하고 엔터(enter)를 치면 getty는 화면에 "password:"라는 프롬프트를 출력 시킨다. getty는 입력된 사용자의 로그인 ID와 패스워드가 일치 하는지 확인을 위해 login을 호출한다. login은 /etc/passwd 파일의 내용과 입력된 사용자 명과 패스워드가 일치 하는지 검색한다. 일치가 되면 login은 사용자를 그의 홈 디렉토리에 위치 시키고, 작업 환경을 정의한 변수들로 해서 초기화 환경을 설정하다. 이들 변수들은 나중에 shell에 전달된다.

보통 초기화 변수들은 HOME, SHELL, USER, LOGNAME 들이며 /etc/passwd 파일에서 찾아진 정보에 의하여 값이 지정된다. HOME 변수에는 사용자의 홈 디렉토리, SHELL에는 로그인 shell, USER 또는 LOGNAME에는 사용자 로그인 이름이 지정된다. 이후 login 프로그램은 시작 프로그램에게 제어를 넘긴다. 여기에서 시작프로그램이란 사용자의 기본 shell(또는 로그인 shell)을 의미한다.(그림 3 참조)

shell의 활성화 과정
[그림3] shell의 활성화 과정



1-4. 명령어의 해석


[그림 3]는 사용자가 시스템에 접속을 하여 shell이 활성화 되기까지의 과정을 보여주고 있다. 이것은 shell이 메모리에 로드가 되어지기 전이다. 그래서, 사용자가 정상적으로 로그인 하였으면, /etc/passwd 파일에 설정되어 있는 로그인 shell에 따라 Bourne, Korn, Cshell을 메모리에 로드하여 실행을 하게 된다.([표22-1] shell 경로 참조)

다음은 [그림 3]의 (1)에서 발생하는 과정을 나열한 것이다.

   - 각 사용자의 홈 디렉토리에 있는 초기화 파일(각 shell의 시작 파일)을 읽어 실행
   - 각 shell의 프롬프트를 표시하고, 사용자의 명령을 기다린다.
   - 사용자가 명령어를 입력하면, 이를 해석하여 커널에게 전달
   - shell은 종료 값이 입력되면 shell은 이를 입력의 끝으로 해석하여 shell을 종료한다.

위 내용 중 세 번째의 내용은 실제로 처리를 하는 부분으로서, 다음의 과정을 포함한다. 이를 달리 shell의 기능이라 할 수 있다.

   a. 사용자의 입력을 기다리는 shell 프롬프트를 출력(명령어 입력 대기)
   b. 명령어 라인에서 명령어를 입력
   c. 명령어 처리(명령어의 해석 및 실행)
   d. 명령어나 프로그램의 처리가 끝난 후 다시 명령어 입력을 기다 리는 shell의 제1 프롬프트를 출력

shell은 shell이 종료 하기 전까지 a ~ d 과정을 계속 반복을 한다.

프로세스의 경우에는 실행이 되는 하나의 프로그램을 말하며 고유 PID(Process Identification) 번호로 확인이 된다. 시스템 커널이 이들 프로세스를 제어하고 관리를 하게 된다. 하나의 프로세스는 해당 실행 프로그램, 데이터, 스택, 프로그램 포인터, 스택 포인터, 레지스터 및 프로그램을 구동시키기 위해 필요한 정보들로 구성된다. 예를 들어, shell을 시작하는 것 자체가 하나의 프로세스이다. 따라서 shell도 해당 그룹의 PID 번호를 갖게 된다. 그리고, 한 번에 하나만이 프로세스 그룹의 터미널을 제어한다. 그리고, 이 프로세스가 포그라운드에서 구동되고 있다고 말한다. 사용자가 로그온 하면 사용자의 shell이 터미널을 제어하게 되고 프롬프트에서 코맨드가 입력되기를 기다린다.

Shell은 다른 프로세스를 만들 수 있다. 입력된 명령어나 스크립트를 내장 코드나 디스크에서 실행시켜줄 책임을 갖는다. 이 작업은 커널에 호출하는 것으로 이루어진다. 하나의 시스템 호출은 커널의 서비스를 요청하는 것으로 하나의 프로세스가 시스템의 하드웨어에 접근할 수 있는 유일한 방법이다.

다음 그림은 shell과 명령어가 입력되어 실행 되어지고 다시 shell에게 그 권한을 넘겨 주기까지의 과정을 간략히 표현한 것이다

shell과 명령어 실행
[그림 4] shell과 명령어 실행



1-5. shell 변환


shell 변환이란 사용자가 원하는 shell로 전환하는 것이다. 예를 들면 기본적으로 Bourne shell을 사용하고 있는데 편집의 편리성 때문에 Korn shell을 사용하고자 한다. 이때 프롬프트에서 'ksh'라고 입력 후 엔터(enter)를 치면 korn shell로 바뀌게 된다.

예제] 현재 Bourne shell을 사용하고 있는데, 이를 Korn shell로 전환을 하고자 한다.

   # ps
   PID TTY    TIME CMD
   20962 pts/3    0:00 sh
   20963 pts/3    0:00 ps
   # ksh
   # ps
   PID TTY    TIME CMD
   20962 pts/3    0:00 sh
   20965 pts/3    0:00 ps
   20964 pts/3    0:00 ksh
   #

위의 과정을 보면 sh는 표준 Bourne shell을 의미한다. ksh라고 입력을 함으로써 Korn shell로 전환이 된 것이다. 즉, Bourne shell이 활성화 된 후 ksh를 입력하여 Korn shell이 활성화 된 것이다. 그래서, Korn shell을 종료 하게 되면 처음의 Bourne shell로 돌아 간다. 만약, Korn shell에서 C shell로 바꾸고 싶으면 'csh'라고 입력 하면 되고, 다시 Korn shell로 가고자 한다면 C shell을 종료 하면 된다.

각각의 shell은 자신의 shell 환경을 설정하는 환경 파일들이 있다. 처음 로그인하여 환경이 설정이 되는 것처럼 shell 전환을 할 경우에도 이 환경 파일들이 실행이 되어 해당 shell의 환경을 설정한다. 다시 설명하자면, 로그인이 이루어 지거나 shell 변환이 있을 경우 shell은 활성화 되고, 활성화 되면서 환경 파일을 실행하여 각각의 shell에 맞는 환경을 설정한다. 로그 아웃 또는 shell을 빠져 나가면 shell은 자동으로 종료가 된다.


1-6. shell 종료


shell의 종료는 시스템에서 로그 아웃(logout) 또는 현재 사용하고 있는 shell을 빠져 나올 경우 shell은 종료가 된다. shell을 종료 하는 방법은 다음 3가지 중 하나를 사용하면 된다.

   (1) <CTRL-D>

   (2) # logout

   (3) # exit


# logout
Connection closed


shell에 따라서는 를 누르면 shell이 종료가 되는 것을 막고, 'exit' 나 'logout'만으로 shell을 종료 시키는 방법이 있다. C shell을 사용하는 사용자라면 'set ignoreeof' 라고 설정을 해두면 를 누르면 'exit'나 'logout'을 사용하여 종료 하라는 메시지를 출력한다.

다음은 각각의 경우에서 로그 아웃 또는 shell을 종료 하는 것을 보여주고 있다.

# exit
로그 아웃

# logout
로그 아웃

# <CTRL-D>
로그 아웃

위에서 shell 프롬프트는 # 이다. Bourne shell이면 $로 표시가 되어야 하나 여기에서는 #로 표시가 되었다. 이는 root 계정 슈퍼 유저에서 사용을 하고 있기 때문이다. 슈퍼 유저 계정에서 사용하면 항상 프롬프트는 #으로 출력이 된다.

이외에도 kill 명령을 사용하는 경우가 있는데 이는 시스템 관리자 즉 슈퍼 유저 만이 사용하여 사용자 접속을 끊을 수 있다.

   # who -a
   :
   root   + pts/9   7월 31 07:46   16448   (:0.0)
   root   + pts/11   8월 21 14:59   25787   (156.7.102.30)
   :
   # kill -9 25787
   #

156.7.102.30이라는 IP 어드레스를 가진 컴퓨터에서 현재의 시스템에 telnet등으로 접속을 하였는데, 이를 시스템 관리자가 kill 명령어를 사용하여 해당 로그인 프로세서를 종료 시켰기 때문에 /dev/pts/11 번을 가진 사용자 단말기는 종료가 된다.

shell을 종료 한다는 것은 사용자 접속이 끊기는 경우, 현재 사용자 계정을 종료, 로그 아웃의 의미와 일맥 상통한다. shell이 종료 되면 커널은 init에 제어를 넘기어 로그인을 재 시작 하도록 한다.

TOP...




[2]shell 프로시져(procedure)와 shell 스크립트(Script)


2-1. shell 프로시져


shell 프로시져는 하나의 shell 프로그램 파일을 의미한다. 이 파일은 다양한 UNIX 명령어와 shell 명령어로 구성이 되는 프로그램 파일로서 마치 UNIX 명령어처럼 사용할 수 있다. shell 명령어들을 하나의 텍스트 파일에 저장하여 이 파일에 대해 실행 권한을 주어서 일반적인 UNIX 명령어를 실행 하듯이 실행하여 원하는 결과를 얻을 수 있다. 이러한 프로시져들을 shell 스크립트(script)라고 표현하기도 한다. 일반적으로는 프로시져라는 표현 보다는 스크립트라는 표현을 더 많이 사용한다.

그리고, shell 프로시져는 복잡한 명령어를 간단히 할 수 있고, 표준 UNIX 명령어를 최적화 할 수 있으며, 사용자로 하여금 새로운 실행 파일(또는 명령어)를 생성 할 수 있게 해준다. shell 프로시져는 처음 생성이 될 때 실행 허가를 가지고 있지 않다. 해서 chmod라는 명령어로 실행 모드를 주어 파일 명만 입력하면 바로 실행이 되게끔 해준다. 실행하는 방법은 크게 다음 두 가지가 있다.

파일에 실행 권한을 주어서 바로 실행 :
# chmod 755 filename
# filename


파일명 앞에 'sh'를 둔다.
# sh filename



예제] test01 이라는 파일을 생성하여, 이 파일을 실행하여 결과를 얻기까지의 과정이다.
   # vi test01
   echo "This is a test1 file"
   date
   # chmod 755 test1
   # test01
   This is a test1 file
   2002년 10월 14일 월요일 오전 10시 20분 52초
   #

위 예제는 test1이라는 파일을 생성하여 실행 모드를 주어서 해당 결과를 보여주고 있다. test01 이라는 shell 스크립트는 echo 문에 의하여 내용을 출력하고, 현재의 날짜와 시간을 출력한다.

위 내용 중
# chmod 755 test01
# test01


은 다음과 같이 한 번의 입력으로 해결 할 수 있다.

# sh test01

새로운 프로시져를 생성하여 계속 사용하고자 한다면 실행 모드를 주어 사용하는 것이 편리하고, 단 한번만 수행을 하고자 한다면 'sh' 문을 사용하면 편리할 것이다.

스크립트는 제어구조, 루프(loop),등 다양한 형태의 프로그램 구조를 가진다. shell에 따라 차이는 있지만 방법은 거의 비슷하고, 해당 내용은 각각의 shell에서 상세히 보기로 한다.

스크립트를 작성할 경우에는 어떤 어떠한 shell을 기준으로 하여 작성을 하는지 결정하고, 해당 shell의 문법에 맞게끔 프로그래밍을 하여야 한다. 해서 스크립트의 첫 줄에 어떠한 shell을 사용하는지 정의를 해두는 것이 좋다. 이는 좋은 프로그램을 작성하기 위한 방법이고, 프로그램을 이해하는데 도움이 될 것이다.

다음은 스크립트의 첫 줄에 입력이 되는 규칙을 상세히 설명한 것이다.

   1) 첫 줄이 #만 있으면 C shell이다

   2) 첫 줄이 #!pathname이면 해당 shell
      #!/bin/sh   Bourne shell
      #!/bin/ksh   Korn shell
      #!/bin/csh   C shell

   3) 위 1과 2가 아니면 무조건 Bourne shell로 해석


만약 첫 줄이 아니고 두 번째 줄에 #이 있다면 이것은 주석문으로 해석이 된다.

그리고, shell 스크립트의 이름을 줄 경우, Bourne shell로 작성 했으면 filename.sh, Korn shell로 작성 하였으면 filename.ksh, C shell로 작성 하였으면 filename.csh 등의 확장자를 두면 shell 스크립트를 쉽게 이해할 것이다.

예제] 다음의 color.sh 는 Bourne shell로 짜여진 스크립트이다. 그런데 이 스크립트의 첫 줄에 #!/bin/csh 라고 정의를 한 후 실행을 하였을 경우와 이를 Bourne shell로 정상적으로 설정 했을 경우이다.
   # cat color.sh
   #!/bin/csh
   for i in 1 4 5 6 7 31 32 33 34 35 36 41 42 43 44 45 46
   do
   echo "\033[${i}m SKY1004 \033[0m"
   done

   # color.sh
   for: 명령어가 없음
   do: 명령어가 없음
   i:   정의 되지 않은 변수

에러가 뜨는 이유는 color.sh 파일 안의 내용 중 'for' 문의 형식이 Bourne shell 형식인데, 처리를 C shell로 하게끔 첫 줄에 정의가 되어 있기 때문이다. 이 문제의 해결은 파일의 첫 줄을 수정 하거나 'for' 문을 C shell에 맞게 수정을 하면 된다. 다음은 첫 줄의 내용을 '#!/bin/sh'로 수정한 경우이다.

   # cat color.sh
   #!/bin/csh
   for i in 1 4 5 6 7 31 32 33 34 35 36 41 42 43 44 45 46
   do
   echo "\033[${i}m SKY1004 \033[0m"
   done

실행을 하면 'SKY1004' 라는 문장이 다양한 색깔을 띄우며 출력이 될 것이다.

color.sh의 실행
[그림 5] color.sh의 실행



2-2. shell 스크립트(script)의 생성과 실행


shell 스크립트의 생성은 UNIX에서 제공하는 에디터 등을 사용하여 일반 텍스트 파일을 생성 하듯이 만들면 된다. 특이한 것은 실행이 되기 위해 컴파일을 하지 않고, 실행이 되어지도록 실행(execute)허가가 추가 되어진다는 것이다.

스크립트의 생성에서부터 실행하기까지의 순서는 다음과 같다.

1) 에디터를 사용하여 생성할 파일을 연다. 대표적으로는 vi를 사용하고, filename은 파일 내용에 맞게끔 임의적으로 준다.
   # vi filename

파일의 내용을 입력하고 에디터를 종료한다.

2) 파일에 실행 모드의 부여
   # chmod 755 filename

3) 파일의 실행
   # filename <Enter>

위 (3)과 (4)를 한 번에 실행 할 수 있다.

   # sh filename

다음 예제를 보면서 shell 스크립트를 실행을 통하여 위의 과정을 이해 하시기 바랍니다.

예제] 현재의 날짜 및 시간을 출력하고 현재 작업 디렉토리의 파일 목록을 출력하는 shell 프로시져(또는 스크립트(script))이다.
   # cat test02.sh
   echo "오늘의 날짜와 시간은 : "
   date
   echo "현재 디렉토리의 files : "
   ls -a

   # chmod 777 test2.s

   # test2.sh
   오늘의 날짜와 시간은 :
   2002년 10월 15일 화요일 오후 04시 04분 33초
   현재 디렉토리의 files :
   .   .X11-unix   audio.var   test02.sh
   ..   .pcmcia   test01.sh   ttday.sh
   



2-3. shell 스크립트(script)의 실행 과정


앞에서 shell 스크립트의 작성과 실행을 해 보았다. 그러면, 이들 shell 스크립트들은 실제 어떻게 실행이 되어 지는지 자세히 알아 보자.

처음 사용자가 UNIX 시스템에 로그인을 하게 되면 로그인 shell에 맞는 프롬프트를 출력하여 명령어 입력 대기 상태가 된다. 이후 위에서 작성한 스크립트를 수행을 하게 되면 shell은 실행한 스크립트에 대해 해당 프로세스를 호출한다. 이때 시스템은 호출된 프로세스를 메모리에 적재(load)하여 실행을 한다. 이때, 실행이 되는 프로세스는 프로그램, 환경(변수와 그 값들), 레지스터들, 파일 지시자 테이블, 현재와 루트 디렉토리의 이름 등의 정보 등을 포함하는데 이를 프로세스 이미지라고 한다.

처음 실행이 되는 shell 프로세스를 부모 프로세스(parent process)가 한다. 실제적으로 shell 스크립트에 있는 명령어를 해석하는 프로세스는 자식 프로세스라는 것이 한다. 이때 자식 프로세스는 부모 프로세스가 가지고 있는 모든 이미지 정보를 그대로 이어 받아서 직접적인 실행을 한다. 즉, 처음 실행이 되는 shell 프로시져는 shell 프롬프트를 출력하고, 해당 프로세스의 이미지를 자식 프로세스에게 전달하여 실제적인 명령어 라인의 해석 및 수행은 자식 프로세스가 수행한다. 이 자식 shell을 서브(sub) shell이라고 한다.

예제] 다음은 test03.sh 라는 스크립트를 수행한 경우를 보자
   # cat test03.sh
   date
   pwd
   # chmod 755 test03.sh
   # test03.sh
   2000년 3월 23일 목요일 오전 10시 20분 52초
   /home1/lsoo

위 내용에서 'test03.sh'를 수행을 하면 바로 결과가 출력되었는데, 실제로는 'test03.sh'는 다음과 같이 동작을 한 것이다.

로그인 shell은 사용자의 요구에 의해 'test03.sh' 라는 파일 이름을 수행한다. 이때 실행이 되는 프로세스는 부모 프로세스가 된다. 부모 프로세스는 자신의 이미지를 그대로 갖는 자식 프로세스를 생성한다. 자식 프로세스는 내용을 실행하기 위해 각각의 라인을 해석하여 메모리에 적재(load) 한다. CPU는 이 내용을 처리(processing)하여 해당 내용에 대한 결과를 출력한다. 부모 프로세스는 자식 프로세스를 생성하고 자신이 가지고 있는 이미지를 넘겨주는데 이를 'fork' 한다고 표현한다.

컴파일 된 프로그램의 경우 부모 shell은 명령어 라인을 해석하고, 자식 shell을 생성 하지 않고, 자식 프로세스라는 것을 생성하여, 자식 프로세스로 하여금 실행을 하게 한다. 즉, 자식 shell은 스스로 파괴되며 컴파일 된 프로그램을 실행한다.

다음의 예제의 test04.sh 라는 스크립트가 실행이 되는 과정을 상세히 기술한 것이다.

예제] 다음은 test4.sh 라는 스크립트의 실행이 되는 과정을 상세히 설명한 것이다.
   # cat test04.sh
   who
   date


   # test04.sh
   root   console   10월 16 08:20   (:0)
   Kim   pts/4   11월 4 07:24   (:0.0)
   hong   pts/3   11월 3 18:58   (:0.0)
   2000년 3월 23일 목요일 오전 10시 40분 25초


   1) 로그인 shell은 부모 프로세스를 생성하고, 부모 프로세스의 이미지를 갖는 자식 프로세스를 생성하여 who와 date를 해석

   2) 자식 shell은 명령어를 해석하고 각 명령어의 실행을 위해 손자 프로세스를 생성한다. who와 date는 컴파일 된 프로그램이고 같은 명령어 파일 이기 때문에 who와 date는 같은 레벨에서 수행한다.       who      대체할 새로운 프로세스를 생성하고
      who를 이로 대체
      date      대체할 새로운 프로세스를 생성하고
      date를 이로 대체

3) who와 date 명령어가 실행 될 때, 세 개의 shell 프로세스(부모, 자식, 손자)를 갖는다. 결과적으로 who와 date는 손자 프로세스에 의하여 실행되며, 자식 프로세스는 who와 date가 실행 종료 시 종료된다.
br>

TOP...




[3]각 쉘(shell)의 관계


가장 많이 사용하는 본 쉘((Bourne shell), 콘 쉘(Korn shell), C shell을 기술한다. 다음의 그림처럼 Korn shell은 Bourne shell이 가지고 있는 기능에 더 확장된 기능을 더 가지고 있다.

각 shell의 관계
[그림 6] 각 shell의 관계


위 그림처럼 Korn 쉘은 Bourne 쉘이 포함하고 있는 내용을 그대로 가지고 있다. 그렇다고 C 쉘과 Korn / Bourne 쉘이 전혀 상관이 없는 것이 아니다. 부분적으로 겹치는 부분도 있다. 다음은 각각의 쉘을 비교한 표이다.

<

[표5] 각 shell의 비교
특징Bourne shell C shellKorn shell
UNIX에서 표준 shellYES NONO
syntax가 Bourne shell - NOYES
Job controlYES YESYES
History listNO YESYES
Command-line editingNO YESYES
AliasesNO YESYES
Single-character abbreviation for login directoryNO YESYES
Protection from overwriting( noclobber )NO YESYES
setting to ignore Control-d ( ignoreeof )NO YESYES
Enhanced cdNO YESYES
Initialization file separate from profile NOYESYES
Logout fileNOYES NO


UNIX 시스템에 처음 로그인 하게 되면 쉘은 변수들을 할당한다. 쉘은 지역(local)변수 와 환경(global) 변수를 지원한다. 이 두 종류의 변수는 문자열 형태로서 자료들을 가진다. 변수는 사용자가 그 값을 미리 정의 하여 변수를 정의 하기도 하며, 기본적으로 사용자가 로그인을 하면 자동으로 할당이 되는 표준 쉘 변수도 있다. 변수는 하나의 값을 가진 이름이다. 사용자는 필요에 따라 이들 변수들을 변경할 수 있으며, 사용하지 않는 변수는 설정을 할 필요가 없다.

표준 shell 변수는 대문자로 표현이 되며, 쉘에 의해 미리 할당이 되어 있다. 주로 사용자가 로그인을 하였을 경우 환경을 설정하기 위해 사용이 된다. 또, shell 스크립트(script)가 수행이 될 때, 자식 프로세스에게 이미지가 전달되는데, 주로 이 환경변수가 전달된다. 해서 환경 변수를 글로벌(Global) 변수라고 한다.

여기에서 지역 변수와 환경 변수의 차이점을 볼 수 있다. 만약 현재의 쉘이 다른 쉘(이를 서브 쉘 또는 자식 쉘이라 한다)을 호출을 할 경우, 자식 쉘은 그 부모 쉘이 가지고 있는 모든 환경 변수의 값을 그대로 복사하여 얻는다. 지역 변수에 대해서는 그 복사본을 얻지 못하고, 자식 쉘이 호출 되어 질 때 초기화 되어진다. 이때 자식이 가지는 환경 변수는 부모 쉘과 자식 쉘 사이에서 유용한 정보를 전달하기 위해 사용이 된다.

다음의 예제와 그림은 이의 내용을 이해 시켜 줄 것이다.

   # id
   uid=0(root) gid=1(other)
   # echo $USER
   root
   # ksh
         -> Korn 쉘 호출(서브 쉘 호출)    # echo $USER
   root

위 내용은 USER라는 표준 변수는 현재 root(슈퍼 유저 계정)인데, Korn쉘을 서브 쉘로 호출 하였을 경우에도 USER라는 환경 변수는 root라는 값을 가지고 있다.



shell 이미지 복사
[그림 7] shell 이미지 복사


사용자가 설정하는 변수는 사용자가 필요에 의해 생성하는 변수이다. 이러한 사용자 정의 변수들의 생성과 사용, 삭제 등은 이 장의 뒷 부분에서 상세히 다룬다.

TOP...




[4]주석문과 파이프


4-1. 주석문


주석문은 실행을 하지않고, 단지 해설을 첨부하는 기능이다. C 언어로 프로그램을 작성할 때 각각의 라인마다 설명을 달아 프로그램의 이해를 돕고 있다. C 프로그램에서는 이 주석문이 컴파일이 되지 않는다. shell 에서도 마찬가지로 C 프로그램과는 달리 주석문은 shell에 의해 해석이 되지만 실제로는 수행은 되지 않으면서 설명을 첨부하여 프로그램의 이해를 돕는다.

UNIX shell에서는 주석문으로 설정하려면 해당 문장 앞에 '#' 부호를 삽입하면 되며, 스크립트가 실행이 될 때 shell은 파일의 각 라인을 읽고, 라인 별로 실행을 하는데, '#'다음의 문장들은 실행 되어지지 않는 설명문이라고 해석을 한다.

예제] 주석문(comment) 연습
   # cat test05.sh
   # 이 문장은 주석문 입니다 #
   # 여기도 주석문 입니다 #
   clear # 화면을 깨끗이 지운다. DOS의 cls와 같음
   echo " 안녕하세요 : " # echo 문 연습입니다.
   pwd      # 현재 디렉토리를 출력하는 커맨드
   date       # 현재의 날짜 시간을 출력
   ######################################
   ### 주석문 처리 연습 스크립트입니다 ###
   ### 주석문은 실행이 되지 않기 때문에 ###
   ### 결과가 화면에 나오지 않습니다 ###
   ######################################
   # test05.sh
    안녕하세요 :
   /home/kim
   2000년 3월 23일 목요일 오전 11시 19분 10초
   #

위 예제를 보면 문장 앞에 '#'으로 되어 있으면, shell은 이를 주석문으로 처리하여 실제 실행 결과에는 출력이 되지 않는다.


4-2. 파이프(pipe) 개념


shell에서 많이 사용되는 것 중에 하나가 파이프이다. shell에서 파이프라는 것은 한 프로세스의 표준 출력을 다른 프로세스의 표준 입력으로 사용하는 것을 말한다. 이를 위해 | 라는 특수 기호를 사용한다. 사용되는 형식은 다음과 같다.

   # 명령어1 | 명령어2


위의 형식처럼 명령어1의 결과의 출력을 파이프를 거쳐 이를 명령어 2의 표준 입력으로 사용하는 것이다.

파이프를 사용하면 다양한 UNIX 명령어를 사용하여 해당 결과 중 자신이 원하는 것만 파이프를 통해 나오는 것을 거를 수 있다. 파이프는 표준 출력이 되는 내용을 그대로 다음 명령어의 표준 입력으로 사용을 하는 것이지, 함수처럼 어떤 작업을 처리 하는 것이 아니다. 다음의 예를 보자.

   # ps -ef | grep nfs

위 내용을 해석하면 'ps -ef'의 결과 중 nfs관련 프로세스만을 grep으로 검출하는 것이다. 즉, 'ps -ef'의 결과를 그대로 'grep nfs'의 입력으로 주는 것이다.

   # ls -l | wc -l

위의 예는 'ls -l'은 현재 디렉토리의 목록을 길게 보여준다. 이의 결과를 'wc -l'로 주어서 라인 개수를 세는 것이다. 즉, 현재 디렉토리의 파일 수를 세는 것이다.

   # ps -ef | grep nfs | wc -l

위의 내용은 'nfs'가 들어 있는 프로세스의 수를 보여준다. 이 경우에는 다양하게 사용할 수 있는데, 관련 프로세스가 몇 개가 동작 중인지 알고 자 할 경우 사용하면 유용하다.

   # vmstat -S 2 | tee /home/vmstat.txt

vmstat라는 명령어는 시스템 성능을 측정하는 명령어이다. 2초 간격으로 성능 측정을 하는데 출력 파이프 라인을 통해 tee명령어의 입력으로 들어간다. tee 명령어는 이를 받아서 화면에 출력시킴과 동시에 파일 /home/vmstat.txt 에 결과를 write한다.

파이프의 사용은 UNIX를 효율적으로 사용하기 위한 하나의 방법이다. 앞으로 이 파이프의 사용은 사용자에게 있어서 UNIX 가이드가 될 것으로 본다.

TOP...




[ 요약 ]


[1] shell(shell) 이란, 사용자와 커널 사이에서 인터페이스 역할을 해주는 UNIX 프로그램이다. 그리고, 다음의 기능을 가지고 있다.
   - 사용자와 커널사이의 인터페이스(interface) 역할

   - 사용자가 입력한 명령어를 읽고 해석하는 프로그램(명령어 해석기 : command processor)

   - 해석형 프로그래밍 언어

     -> shell 스크립트(script 또는 프로시져(prodecure))라는 shell 명령과 문장으로 이루어진 파일
     작성 가능 사용자는 자신의 shell 스크립트(script)를 생성할 수 있고, 이 스크립트(script)의
     실행은 사용자가 표준 UNIX 명령어를 입력하는 것과 같은 방법으로 수행


[2] 로그인 shell이란 사용자 shell로서 사용자가 시스템에 로그인을 하였을 때 자동으로 실행이 된다. 시스템 관리자에 의해 /etc/passwd 파일에 설정이 되며, 사용자는 필요에 의해 자신의 shell을 변경할 수 있다.


[3] shell은 getty에 의해 사용자 ID와 패스워드를 입력 받아 이것이 /etc/passwd 파일에 있는 내용과 일치하는지 검색하기 위해 login을 호출하고, 일치하면 login은 사용자를 그의 홈 디렉토리에 위치 시키고, 사용자 shell을 시작 시킨다.


[4] 사용자 shell 변환은 사용하고픈 shell의 실행 명령어를 입력하면 된다. C shell로 바꾸고 싶으면, 'csh'라고 입력하면 되고 이를 종료 하려면 shell을 종료 하는 방법을 사용한다.


[5] shell을 종료 하는 방법은 다음 3가지가 있다.

      # logout      # exit


[6] shell을 종료 한다는 것은 사용자 접속이 끊기는 경우, 현재 사용자 계정을 종료, 로그 아웃의 의미와 일맥 상통한다. shell이 종료 되면 커널은 init에 제어를 넘기어 로그인을 재 시작 하도록 한다.


[7] shell 스크립트란 UNIX가 제공하는 명령어와 shell이 제공하는 명령어를 가지고 작성한 하나의 프로그램으로서 사용자가 원하는 결과를 쉽게 얻을 수 있게 해준다.


[8] shell 스크립트를 실행 하기 위해서는

    # sh filename

    # chmod 755 filename

   # filename


[9] 주석문은 해석은 되지 않고 shell 스크립트에서 설명을 덧붙이기 위해 사용된다.


[10] 파이프(pipe)란, 기호로는 '|'를 사용하고, 한 명령어의 출력 결과가 다른 명령어의 입력이 되게끔 연결해 준다.

TOP...