Bourne Shell에서의 명령어 라인 처리과정 I
쉘 프로그램을 작성하기 전에, 명령어를 실행되는 동안에 쉘에 의해서 수행되는
처리과정을 이해하는 것이 매우 중요하다.
터미널에서 입력한 모든 라인은 실행을 위해서 쉘에 의해 분석된다.
이때 단말에서 입력한 라인을 명령어 라인이라 하며, 기본적으로 다음과 같은
형식을 갖는다.
┌────────────────┐
│ $Program_name arguments ... │
└────────────────┘
명령어가 커널에 의해 실행되기전, 쉘은 입력라인을 사전에 정의된 번역 절차를
수행한다. Bourne Shell과 Korn Shell은 서로 다른 번역 절차를 수행하는데,
이후 Bourne Shell과 Korn Shell의 명령어 라인 처리 과정을 각각 강의한다.
우선 Bourne Shell에서의 명령어 라인 처리과정을 설명하기에 앞서,
전체의 번역 과정을 도표로 소개한다.
┌─────┰───────────────────────────┐
│실행 단계 ┃ 실행 내용 │
├─────╂───────────────────────────┤
│ 1 ┃ 입력의 읽기(read)및 분석(parse) │
├─────╂───────────────────────────┤
│ 2 ┃ 파라메터 대치(Substitution) │
├─────╂───────────────────────────┤
│ 3 ┃ 명령어 대치(Substitution) │
├─────╂───────────────────────────┤
│ 4 ┃ 입출력 방향전환(Redirection)과 파이프(Pipe) 처리 │
├─────╂───────────────────────────┤
│ 5 ┃ IFS(Internal Field Separator) 처리 │
├─────╂───────────────────────────┤
│ 6 ┃ 화일명 대치(Substitution) │
├─────╂───────────────────────────┤
│ 7 ┃ 환경 처리 │
├─────╂───────────────────────────┤
│ 8 ┃ 보호문자 제거 │
├─────╂───────────────────────────┤
│ 9 ┃ 명령어 실행 │
└─────┸───────────────────────────┘
이 과정을 설명하기 위해 단말로 부터 다음과 같은 라인이 입력되었다고
가정하여 이후 각 단계를 설명한다.
┌────────────────────────┐
│$f="The files are: "; echo `pwd` $f * > outfile │
└────────────────────────┘
1. 입력의 읽기및 명령어 라인 분석
입력은 newline(\n), 세미콜론(;), 백그라운드(&), 논리곱(&&) 또는
논리합(||) 문자들을 만날때까지 데이타 화일이나 단말의 명령어 라인으로
부터 읽어들인다.
만약 명령어가 단순한 변수 할당이 아닌 경우에는, 입력은 각각의
단어(Word)별로 분석된다. 이러한 분석 작업은 공백과 탭을 기준으로
행해진다. 이때 보호문자에 둘러쌓인 공백이나 탭 문자는 처리의 대상이
되지 않는다.
이 단계에서는 위에서 전제한 명령어 입력을 읽어들인 후, 입력 라인을
개별적인 명령어로 분석하는 작업이 행해진다. 그 결과는 다음과 같다.
┌───────────────────────┐
│ 명령어별 분석 │
┝━━━━━━━━━━━━━━━━━━━━━━━┥
│ f="The files are: " │
├───────────────────────┤
│ echo `pwd` $f * > outfile │
└───────────────────────┘
이후 분석된 결과의 첫번째 명령어인 변수 f의 할당이 우선 별도로 행해진후,
두번째로 지정된 echo 명령어의 처리가 개시된다. 이때 echo 명령어를
쉘은 단어별로 분석하는 작업을 수행하는데, 수행 결과는 다음과 같다.
< echo 명령어의 분석 결과 >
┌───┬───┬───┬───┬───┬────┐
│echo │`pwd` │$f │* │> │outfile │
└───┴───┴───┴───┴───┴────┘
쉘은 위의 결과를 갖고 다음 단계의 작업을 수행한다.
2. 파라메터 대치(Substitution)
이 단계에서는 명령어 라인에 지정되어 있는 포지셔널 파라메터 대치,
변수 대치 그리고 특수 변수 대치가 쉘에 의해 수행된다.
이 단계에서 대치되는 파라메터들에는 모두 $ 기호가 선행한다.
전 단계의 결과를 보면, 파라메터로서 $f가 선언되어 있다.
쉘은 $f를 대신해서, 실제 f 변수에 선언되어 있는 변수값으로 대치 작업을
수행한다. 대치 작업 수행후의 결과는 다음과 같다.
┌───┬───┲━━━━━━━┱───┬───┬────┐
│echo │`pwd` ┃The files are ┃* │> │outfile │
└───┴───┺━━━━━━━┹───┴───┴────┘
변환후 다음 단계가 계속된다.
3. 명령어 대치(Substitution)
한쌍의 역따옴표(Grave accents(`))내에 지정되어 있는 문자열에 대해서는,
쉘에 의해 실행 명령어로 인식되어 처리된다.
바로 이 단계가 역따옴표내에 기술되어 있는 명령어를 실행하는 단계이다.
실행된 명령어로부터의 출력이 본래의 위치에 명령어를 대신해 위치된다.
역따옴표내에는 그룹화, 파이프 라인, 연속된 명령어들을 기술될 수 있다.
그리고 명령어 실행 결과에 들어 있는 불필요한 공백, 탭 그리고 newline
문자들은 쉘에 의해 제거된다.
주의) 역따옴표(`)와 따옴표(')를 혼돈하지 말아야 한다.
따옴표는 특수문자의 처리를 쉘이 수행하지 못하도록 하는데
사용되는 또다른 특수문자이다.
이 단계의 실행 결과는 다음과 같다.
┌───┲━━━━━━┱───────┬──┬───┬─────┐
│echo ┃/home/user1 ┃The files are │* │ > │ outfile │
└───┺━━━━━━┹───────┴──┴───┴─────┘
4. 입출력 방향전환(Redirection)과 파이프(Pipe) 처리
다음은 입출력 방향전환을 나타내는 기호들이다.
┌─────────┬──────────┬──────────┐
│ 방향전환 심볼 └─┐ 처리 내용 │ 미정의시 기본값 │
│(Redirection Symbol) │ │ (Default) │
├─────────┬─┴────────┼──────────┤
│ 0< 또는 < │ 입력 방향전환 │ 터미널 키보드 │
├─────────┼──────────┼──────────┤
│1>,1>>,> 또는 >> │ 출력 방향전환 │ 터미널 스크린 │
├─────────┼──────────┼──────────┤
│2>,2>>,> 또는 >> │ 에러 방향전환 │ 터미널 스크린 │
└─────────┴──────────┴──────────┘
위 도표에서 방향전환 심볼에 사용된 숫자는 각각의 입출력 종류와
연관된다. 이 숫자는 프로그램에서 사용되는 각각의 화일들을 구별하기
위해 사용하는, 화일 기술부(File Descriptor Table)내의 인덱스 번호인
화일 기술자(File Descriptor)를 의미한다.
이 테이블내의 각각의 인덱스들은 0,1,2,3,4 등의 정수로 구별되는데,
각각의 인덱스에는 프로그램내에서 사용하는 각각의 화일들이 등록된다.
이중 0,1,2번은 시스템에 의해 표준입력, 표준출력, 표준에러를 저장할
화일들이 등록된다. 즉 이를 그림으로 설명하면 다음과 같다.
┌─────┐
┌────┤ FDT │
│ ├─┰───┤
│ │0 ┃ ──────────── 터미널 키보드
│ ├─╂───┤ │
프로그램에서 │1 ┃ ───────── 터미널 스크린 │
사용하는 ├─╂───┤ │ │
화일을 등록 │2 ┃ ─────── 터미널 스크린 │ │
│ ├─┸───┤ │ │ │
│ │ . │ └───┼───┘
└────┤ . │ │
│ . │ 사용자가 이 값들을
└─────┘ 방향전환 심벌을 이용하여
바꿀수 있게된다.
만약 방향전환 심볼 사용시 입출력 방향이 지정되지 않으면,
도표내에 기술되어있는 디폴트가 가정된다. 또한 방향전환 심볼에
화일 기술자가 지정되어 있으면, 디폴트로 선언돼있는 화일이 아닌,
지정한 화일로 또는 화일로 부터 입출력이 행해진다.
또 파이프가 지정되어 있는 경우에는 첫번째 지정된 명령어의 표준출력이
파이프 다음에 지정되어 있는 명령어의 표준 입력으로 전달된다.
┌─┬────┐
│0 │키보드 │
┌───┬──────┬───────┬──┐ ├─┼────┤
│echo │/home/user1 │The files are │* ┝━━> │1 │outfile │
└───┴──────┴───────┴──┘ ├─┼────┤
│2 │터미널 │
이 결과에서 출력 방향전환 심볼에 의해 ├─┼────┤
화일 기술자 1번에 등록된 화일이 outfile로 │. │ . │
변경된것을 주목하기 바란다. │. │ . │
이 변경은 현재의 실행 단계에서는 아무런 영향을 └─┴────┘
미치지 않으나, 명령어 실행후 생성되는 출력 처리시에 여기서 지정한 화일
즉 outfile이 사용된다.
Bourne Shell에서의 명령어 라인 처리과정 II
5. IFS (Internal Field Separator) 처리
다음 단계로 IFS 처리가 수행된다.
입력은 명령어, 화일명 또는 변수 대치로 인해 입력시의 상태와 달라지게
되기 때문에 IFS를 기준으로 재분석된다. IFS 변수는 명령어 라인내의
각 필드를 구별할때 사용되는 문자들이 정의되어 있다.
기본적으로(디폴트로) IFS 변수에는 공백, 탭 그리고 newline 문자가
정의되어 있다. 명령어 라인상에 지정되어있는 IFS에 정의되어 있는 문자들은
각 필드를 구별하기 위해 공백으로 대치된다. 그러나 보호문자(따옴표,
쌍따옴표등)로 둘러 쌓인 IFS 문자들은 쉘의 처리로부터 보호된다.
만약 IFS가 사용자에 의해 변경되었다면, IFS가 공백 문자에 더하여
지정한 값이 할당된다. 예를들면:
"" $IFS=: "" 라고 지정하면 공백에 더하여 IFS에 :을 할당한다.
이번 단계에 대한 예제는 충실한 예를들기 위해서 지금까지와는 다른 예를
들고자 한다.
명령어 라인에 다음과 같이 지정되어 있다고 가정하자.
┌─────────────────────────────┐
│ $echo The files in <tab> /home/mjr <tab> are: <tab> * │
└─────────────────────────────┘
이 경우, 이 단계가 수행되면 <tab> 문자는 공백으로 대치되게 된다.
그 결과는 다음과 같다.
┌───────────────────┐
│ echo The files in /home/mjr are: * │
└───────────────────┘
그러나 보호문자내에 이들 IFS 문자들이 지정되어 있을 경우의
처리 경우를 보자.
┌──────────────────────────────┐
│ $echo "The files in <tab> /home/mjr <tab> are: <tab>" * │
└──────────────────────────────┘
이 경우에는 아래와 같이 본래 지정되어 있는 라인 그대로 유지된다.
┌─────────────────────────────┐
│ echo "The files in <tab> /home/mjr <tab> are: <tab>" * │
└─────────────────────────────┘
6. 화일명 전개(Expansion)
이 단계에서 입력 라인이 화일명 메타케릭터(metacharacters)의 존재 여부를
확인하기 위해 검색된다. 이때 이들 문자가 입력 라인에서 발견되면, 쉘은
대상 디렉토리내에서 지정한 조건과 일치하는 화일을 찾는 작업을 수행한다.
일치되는 화일이 발견되면, 발견된 화일의 화일명으로 대치하게 된다.
만약 일치되는 화일이 발견되지 않으면 본래의 메타케릭터들이 그대로
입력 라인에 남게된다.
다음 예제는 화일명 전개후 결과를 보여준다.
( 4단계의 실행 결과 )
┌───┬────────┬───────┬──┐
│ echo │ /work/acct/user│The files are:│ * │
└───┴────────┴───────┴──┘
이 결과에서 보면 화일명 대치를 의미하는 * 문자가 지정되어 있다.
( 화일명 전개 단계의 실행 결과 )
┌───┬────────┬───────┲━━━━━━━━━┓
│ echo │ /work/acct/user│The files are:┃file1 file2 file3 ┃
└───┴────────┴───────┺━━━━━━━━━┛
* 문자가 지정되었던 곳에, 현재의 디렉토리내에 있는 모든 화일명이 대치된
것을 볼수 있다.
7. 환경 처리 (Environment Processing)
이 단계에서는 3가지의 작업이 수행된다.
첫째로, 명령어에서 요구하는 명령어 실행 환경을 구축을 위한 변수들에 대한
할당이 행해진다.
주의) 이때의 변수 할당은 명령어가 실행될때 실행 환경을 설정하기
위해서 명령어 자체에서 변수할당을 요구하는 경우에 명령어상에
지정되어 있는 변수들의 할당을 의미한다.
이 경우의 예로 dd 명령어를 들수 있다.
dd 명령어는 화일의 내용을 변환 또는 복사하는 기능을 수행한다.
그런데 이 명령은 이와 같은 작업을 실행하기 위해 명령어상에서
몇가지 변수할당을 필요로 한다.
다음은 dd 명령어의 예이다.
$dd conv=ucase if=prog1
이 명령은 prog1 화일의 내용을 대문자로 변환한다.
이때 conv와 if는 명령어의 실행 환경을 정의하기 위해
dd 명령어에 의해 필요로하는 변수이다. conv는 변환 종류를
지정하는 변수이고, if는 대상화일을 지정하는 변수이다.
바로 이런 변수들에 대한 할당이 수행되는 단계가
환경 처리 단계이다.
둘째로, 명령어의 위치를 찾기위해 PATH 변수의 검색이 수행된다.
만약 PATH 변수에 지정되어 있는 경로명을 검색하여 명령어가 발견되면,
명령어가 절대 경로명으로 대치된다. 그러나 명령어가 절대 경로명으로
지정된 경우에는, 이와같은 PATH 변수를 사용하는 화일 검색이 수행되지
않고 지정된 절대 경로명이 그대로 사용된다.
셋째로, 포지셔널 파라메터 할당이 수행된다.
8. 보호문자의 제거
쉘 처리의 마지막으로 입력 라인에 남아있는 보호문자가 삭제되고,
명령어는 실행이 개시된다.
입력 라인의 쉘의 처리 결과는 쉘의 실행 플러그를 set -x에 의해
설정함으로써 확인할 수 있다. 이 플러그를 설정함에 의해서, 사용자는 실제로
커널에 전달되는 변환된 입력 라인을 확인해 볼수 있게 된다.
이때 쉘은 변환된 입력 라인의 앞에 +라는 표식자를 붙여 출력하여 본래의
입력 라인과 구별해준다.
다음은 set -x 명령을 사용해서 입력 라인의 변환된 결과를 확인하는
예를 보여주는 예이다.
┌────────────────────────────┐
│ $set -x │
│ $echo "The files in" `pwd` are: * │
│ + pwd │
│ + echo The files in /home/mjr are: bin f1 f2 f3 f4 │
│ The files in /home/mjr are:bin f1 f2 f3f4 │
│ $set +x │
└────────────────────────────┘
위의 예에서 +가 붙어 출력된 라인이 두 라인이 있다.
처음의 라인은 역따옴표(실행 명령어임을 의미)로 지정되어 쉘에 의해
우선 처리하기 위해 커널에 전달된 명령어를 표시하며, 두번째 라인은
echo 명령어를 쉘이 변환하여 커널에 전달될때의 상태를 보여주는 결과이다.
9. 명령어 실행
입력된 명령어 라인은 이상의 단계를 거쳐 변환되고, 실행을 위해 커널에
전달되어, 실행된다. 만약 이 명령어가 유닉스 운영체제의
기본 명령어(Built-in)인 경우에는 현재의 쉘이 이 요구를 처리한다.
다른 경우에는 명령어는 컴파일된 프로그램과 같이 메모리로의 load가
수행되고, load가 정상적으로 되어지면 exec 시스템 호출을 통해 실행된다.
만약 정상적인 load가 수행되지 않으면, fork 시스템 호출이 수행되고,
하위 쉘은 데이타처럼 입력 스크립트를 읽어들여, 입력 스크립트는 쉘에 의해
번역되어 처리된다.
---------------------------------------------------------------------------
Korn Shell에서의 명령어 라인 처리과정
여러 관점에서 Korn Shell은 Bourne Shell과 동일한 방식으로 명령어를
실행하나, 명령어 실행동안 몇가지 부가적인 처리를 더 수행한다.
다음은 Korn Shell에의해 수행되는 명령어 실행 절차를 나타내는 도표이다.
이 도표상에서 우리는 쉽게 두 쉘간의 차이를 확인할 수 있다.
┌──────┰──────────────────────┐
│ 실행단계 ┃ 실행 내용 │
┝━━━━━━╋━━━━━━━━━━━━━━━━━━━━━━┥
│ 1 ┃ 명령어 라인 읽기 │
├──────╂──────────────────────┤
│ 2 ┃ 명령어 라인 분석 │
├──────╂──────────────────────┤
│ 3 ┃ 토큰(Token)들의 분류 │
├──────╂──────────────────────┤
│ 4 ┃ 알리아스(Alias) 대치 │
├──────╂──────────────────────┤
│ 5 ┃ 틸드(Tilde) 확장 │
├──────╂──────────────────────┤
│ 6 ┃ 명령어 대치 │
├──────╂──────────────────────┤
│ 7 ┃ 파라메터 대치 │
├──────╂──────────────────────┤
│ 8 ┃ IFS 처리 │
├──────╂──────────────────────┤
│ 9 ┃ 화일명 확장 │
├──────╂──────────────────────┤
│ 10 ┃ 보호문자 제거 │
├──────╂──────────────────────┤
│ 11 ┃ 명령어 실행 │
└──────┸──────────────────────┘
Korn Shell은 위와같은 과정을 통해 명령어를 실행하게 되는데, 이들 과정은
크게 두 단계로 분류할 수 있다.
첫번째 단계는 입력된 명령어를 읽고, 토큰(Token)으로 분류하는 단계이고,
두번째 단계는 명령어를 확장하는 과정과, 마지막 실행과정까지의 단계이다.
이제부터 Korn Shell에의해 실행되는 처리과정을 설명한다.
그러나 Bourne Shell과 유사한 과정으로 수행되기 때문에 대략적으로 설명한다.
만일 이해가 되지 않는 부분은 "Bourne Shell의 명령어 라인 처리과정" 강의를
참조하기 바란다. 물론 Korn Shell 특유의 처리에 대해서는 자세히 설명할
것이다.
1. 명령어 라인 읽기
제일 처음의 수행단계는 사용자가 요구한 입력을 받아들이는 단계이다.
Korn Shell은 ;, |, &, ||, &&, |& 또는 newline을 만날때까지 명령어 라인을
읽는다. 이때 한가지 우리가 알고 넘어가야 할것이 있다.
물리적 라인과 명령어 라인이 동일하지 않다는 것이다. 단순한 명령어는
하나의 라인에 기술될수 있으나, 복합 명령어들은 여러 라인에 걸쳐서
기술될 수 있기 때문이다. 그러므로 쉘에 의해 받아들여지는 명령어 라인은
하나의 라인이 아니라, 여러 라인도 될수 있다는 것을 인식해야 한다.
만약 Alias 선언과 명령어가 동일한 물리적 라인상에 지정되어 있을 경우에는
같은 물리적 라인상에 지정된 Alias는 명령어에서 참조할 수 없게된다.
그 이유는 Alias는 Alias가 정의된 물리적 라인 이후의 라인에서만 참조할 수
있기 때문이다. 다음은 그 예를 보여주고 있다.
┌─────────────┐
│ KORN> alias p=pwd;p │<- 동일한 물리적 라인상에 지정된 Alias는
│ ksh:p:not found │ 동일 라인상의 명령어에서 참조 불가능하다.
│ KORN> x=Here \ │<- Alias와 명령어가 물리적으로
│ ;echo $x │ 다른 라인에 지정되어 있기 때문에
│ Here │ 참조 명령이 실행됐다.
└─────────────┘
2.,3. 명령어 라인의 토큰별 분석및 분류
일단 받아들인 입력은, Korn Shell이 토큰별로 분석한다.
이때의 분석 단위인 토큰에는 다음과 같은 것들이 있다.
. 입출력 연산자
. 제어 연산자
. Newline 문자
. 예약어
. 변수 식별자
. 단어
. Here Document ( <<char )
주의) 각각의 토큰들간의 구별자로 공백을 사용하지는 않는다.
이유는 연산자와 연이어 기술되는 단어사이의 공백 지정은
선택사항이기 때문이다.(Bourne Shell 부분과 비교하시기
바랍니다.)
위에서 분석된 각각의 토큰들은 다음의 3가지 범주로 분류된다.
. 입출력 방향전환 연산자(연산자 이후에 뒤따르는 단어도 포함한다.)
. 변수 할당
. 명령어 단어
이후 분류된 범주별로 이후 단계별로 처리된다.
4.,5. Alias 대치및 Tilde 확장
입력에 첫번째로 지정된 명령어 단어가 보호문자로 둘러쌓이지 않은 Alias이면,
Alias는 이 싯점에서 Alias에 지정되어 있는 값으로 대치된다.
Ailas 사용시에, Alias 다음에 연이어 공백이 지정되어 있을 경우에는,
Korn Shell은 Alias 실행시 대치되는 명령어에 대한 인자가 뒤따르는 것으로
간주하여, Alias를 실제의 명령어로 대치한다.
이 방식은 nohup과 time 명령어처럼, 인자로써 명령어를
요구하는 명령어에서만 사용한다. 다음은 이에 대한 예이다.
┌─────────────┐
│ $alias myalias='ls' │
│ $myalias /home/mydir │ <- myalias를 실행하면, Korn Shell은
│ . │ Alias에 지정되어 있는 ls 명령어의
│ . │ 인자로써 /home/mydir을 전달하여
└─────────────┘ ls 명령어를 실행하게 한다. 따라서
출력은 현재의 디렉토리가 아닌, /home/mydir 디렉토리의 내용이된다.
Alias 대치후에, Korn Shell은 보호문자로 둘러쌓이지 않은 확장해야할
Tilde(~) 문자가 있는가를 검사한다. 다음은 Tilde 확장 문자들이다.
┌─────┬───────────────────────────┐
│ ~ │ 사용자의 홈 디렉토리의 경로명으로 대치된다. │
│ ~+ │ 현재의 작업 디렉토리의 경로명으로 대치된다. │
│ ~- │ 사용자의 직전 작업디렉토리의 경로명으로 대치된다. │
│ ~username│ 지정한 사용자의 홈 디렉토리의 경로명으로 대치된다. │
│ ~anything│ 대치되지 않는다. │
└─────┴───────────────────────────┘
위를보면, 대치되는 Tilde 확장 문자 다음에는 공백, +, - 또는 username이
뒤따르는 것을 볼수 있다. Korn Shell은 입력 라인에서 Tilde(~) 문자를
발견하면, 연이어 Tilde 확장문자에 뒤이어 공백, +, - 또는 username
지정되어 있는가를 확인하기 위해 다음 문자를 검사한다.
이때 Tilde에 뒤이어 공백, +, - 또는 username이 지정되어 있으면, 그에
상응하는 절대 경로명으로 대치한다.
다음은 Alias 대치및 Tilde 확장 과정을 보여주는 예이다.
KORN> alias l
l=ls -CF
KORN> l ~mjr <----- mjr이라는 사용자의 홈 디렉토리를 의미한다.
bin/ f1 f2 f3 f4 f5 f6 f7 m1* m2* m3* m4*
6. 명령어 대치
Korn Shell은 입력 라인에 $(...) 또는 `...` 형식으로 지정되어 있는
실행해야 할 명령어의 존재 여부를 검사한다. 만일 입력 라인에 이런 형식의
실행 명령어들이 있을 경우에는, Korn Shell은 이들 명령어들을 실행하고,
지정 위치에 명령어의 출력을 위치시킨다.
입력 라인에 명령어 대치를 지정할때 $(cat file)의 형식을 대신하여,
$(<file)이 사용된다. 그 이유는 $(< file)의 지정하면 명령어 처리가
필요로 하지 않기 때문에(즉; cat 명령어의 실행이 필요로하지 않기 때문에)
빠른 처리가 수행되기 때문이다.
이들 명령어들은 서브 쉘 환경에서 실행되는데,이로인해 부모 쉘에는
아무런 영향을 미치지 않는다.
다음은 명령어실행 과정을 보여주는 예제이다.
┌──────────────────────────┐
│ KORN> cat list │
│ user1 │
│ user2 │
│ user3 │
│ nouser │
│ KORN> mail `cat list` < letter <---------------- (1)
│ mail: Can't send to nouser │
│ mail: Return to mjr │
│ KORN> echo "the names in list are: $(cat list)" <---- (2)
│ the names in list are:user1 │
│ user2 │
│ user3 │
│ nouser │
│ KORN> echo "the names in list are: $(< list)" <----- (3)
│the names in the list are: user1 │
│ user2 │
│ user3 │
│ nouser │
└──────────────────────────┘
위의 예에서는 명령어 대치 과정의 실행 예를 보여주고 있다.
(1)번의 예는 cat list 명령어의 실행 결과로 생성되는 list 화일속에
사전 정의되어 있는 사용자들에게 메일이 발송되는 것을 확인할 수 있다.
본래 mail 명령어는 실행 결과에 대한 메세지를 표시하지 않기 때문에
이 예에서는 시스템상에 존재하지 않는 사용자 즉, nouser를 지정하여
에러를 발생시킴으로써 실행 여부를 확인할수 있게 했다.
(2),(3)번의 예는 $(...) 형식으로 명령어 대치를 지정하는 예를 보여준다.
(2)번 형식보다는 (3)번 형식이 효율적이다.
7.,8.,9.,10. 파라메터 대치
IFS 처리
화일명 확장
보호문자 제거
Korn Shell도 Bourne Shell과 동일한 형식으로 이 단계들을 수행한다.
자세한 내용은 "Bourne Shell의 명령어 라인 처리 과정"을 참조하기 바란다.
다음은 이 단계들의 수행 내용을 보여주는 예이다.
┌──────────────────────────────────┐
│KORN> set -x │
│KORN> echo "The files in $PWD <tab> are :" * │
│+echo The files in /home/mjr are : bin f1 f2 f3 f4 f5 f6 f7 m1 m2 m3│
│The files in /home/mjr are : bin f1 f2 f3 f4 f5 f6 f7 m1 m2 m3 │
└──────────────────────────────────┘
set -x 명령을 통해 Korn Shell에의해 변환되어, Kernel에 전달되는
변환된 입력이 + 프롬프트에 연이어 표시되고 있다.
11. 명령어 실행
이 단계에서는 Korn Shell은 화일 입출력 방향전환을 통해 지시된 화일들을
생성한다. 예를들면 " cat list > newfile "이라고 지정했을 경우, 지정된
newfile을 이 단계에서 생성한다.
다음에 Korn Shell은 실행할 명령어가 Built-in 명령어인지를 검사한다.
Built-in 명령어란 유닉스 고유 명령어를 의미한다. 만일 실행할
명령어가 Built-in 명령어인 경우에는 현재의 쉘에서 명령어를 실행한다.
그러나 파이프 라인상에 지정된 Built-in 명령어들은 파이프상의
마지막 명령을 제외하고는 모두 서브 쉘에서 실행된다. 그때문에
파이프 라인상의 Built-in 명령어들은 현재의 쉘에 아무런 영향을 끼치지
않는다. (조금전에 설명했듯이 파이프 라인상의 마지막 명령어는
예외이다.)
다음으로 Korn Shell은 명령어가 function인지를 확인하기 위해 검사하고,
요구된 환경에서 function을 실행한다. Built-in 명령어와 달리, function은
포지셔널 파라메터, function이 호출되었을 때의 행해지는 변수할당,
호출 환경에 의해 설정되는 옵션과 Trap (Trap은 프로그램내에서 특정한
현상이(signal로 감지하게 된다.) 발생했을때 처리방식을 지정하기 위한
명령어이다.)을 이용할 수 없다.
만약 명령어가 Built-in 명령어 또는 function이 아니면, Korn Shell은
해당 명령어를 찾기 위해 PATH 변수에 할당되어 있는 경로명들을 사용한다.
이때 set -h 명령에 의해 Track all 옵션이 설정되어 있고, 명령어 단어가
이미 Tracked alias로 성언되어 있지 않다면, 명령어 단어는 명령어의
절대 경로명을 값으로 갖는 Tracked Alias로 선언된다. 이렇게 선언되는
Tracked Alias는 명령어 실행시 명령어의 위치를 검색하기 위해 소요되는
탐색시간을 줄여줌으로써, 명령어의 실행 속도를 빠르게해주게 된다.
Tracked Alias들은 alias -t 명령을 통해 확인해볼수 있다.
---------------------------------------------------------------------------
쉘 변수를 이용한 작업환경 구축
변수를 이용한 작업환경 구축의 기본사항
이미 유닉스 응용과정 설명시에 유닉스 변수들의 종류, 특징 및 기능들에
대하여 설명했다. 그러므로 이번 과정에서는 쉘에의해 자동으로 생성되어
운영되고, 사용자에 의해 참조가 가능한 쉘의 Reserved Variable및
기능들에 대해서만 간략히 소개한다.
1. Bourne Shell 변수
┌──────┰──────────────────────────┐
│ 변수명 ┃ 설 정 값 │
┝━━━━━━╋━━━━━━━━━━━━━━━━━━━━━━━━━━┥
│ $PATH ┃명령어 검색시 검색 대상 디렉토리의 경로명들 │
├──────╂──────────────────────────┤
│ $HOME ┃사용자 홈 디렉토리의 절대 경로명 │
├──────╂──────────────────────────┤
│ $CDPATH ┃cd 명령어 사용시 인자로 경로명을 지정하지 않았을 │
│ ┃경우에 사용될 디폴트 경로명. │
├──────╂──────────────────────────┤
│ $IFS ┃내부 필드 구별자(디폴트: 공백, newline 그리고 탭) │
├──────╂──────────────────────────┤
│ $MAIL ┃MAILPATH 변수가 설정되지 않았을 경우 메일을 검색할 │
│ ┃화일명 │
├──────╂──────────────────────────┤
│ $MAILCHECK ┃메일을 검색하는 주기(초단위의 시간) │
├──────╂──────────────────────────┤
│ $MAILPATH ┃메일을 검사할 화일들의 목록 │
├──────╂──────────────────────────┤
│ $PS1 ┃기본 프로프트로 사용할 문자열(디폴트:$) │
├──────╂──────────────────────────┤
│ $PS2 ┃보조 프롬프트로 사용할 문자열(디폴트:>) │
├──────╂──────────────────────────┤
│ $SHACCT ┃쉘 계정(Account) 데이타를 저장할 화일 │
├──────╂──────────────────────────┤
│ $SHELL ┃현재 사용중인 쉘(디폴트: /bin/sh) │
└──────┸──────────────────────────┘
2. Korn Shell 변수
위에서 설명한 Bourne Shell의 변수는 Korn Shell에서도 모두 사용가능하다.
또한 Korn Shell은 이에 더하여 사용자의 원할한 작업을 위하여 다음과 같은
몇가지 변수를 추가로 지원한다.
Korn Shell에서 고유하게 지원하는 변수들은 다음과 같다.
┌──────┰──────────────────────────┐
│ $HISTFILE ┃History File의 이름(디폴트:$HOME/.sh_history) │
├──────╂──────────────────────────┤
│ $HISTSIZE ┃History File내에서 억세스할 수 있는 명령어의 │
│ ┃최대 갯수 (디폴트: 128) │
├──────╂──────────────────────────┤
│ $REPLY ┃read 명령어에서 입력을 받아들일때 사용할 디폴트 │
│ ┃변수로 사용된다. │
├──────╂──────────────────────────┤
│ $OLDPWD ┃직전의 작업 디렉토리의 절대 경로명 │
├──────╂──────────────────────────┤
│ $TMOUT ┃단말로부터의 입력이 일정 시간동안 없을 경우에 │
│ ┃단말의 자동적인 logout을 실행할 수 있는데, 이때 │
│ ┃적용할 시간을 지정. 단위는 초이다. │
├──────╂──────────────────────────┤
│ $PS3 ┃select 명령어에서 사용할 프롬프트 │
├──────╂──────────────────────────┤
│ $PPID ┃부모 프로세스 식별자 │
├──────╂──────────────────────────┤
│ $EDITOR ┃Korn Shell의 History 기능에서 사용할 편집기명 │
│ ┃(디폴트: /bin/ed) │
├──────╂──────────────────────────┤
│ $VISUAL ┃EDITOR 변수와 동일, 단 VISUAL이 우선된다. │
├──────╂──────────────────────────┤
│ $_ ┃직전에 수행한 명령어의 마지막 인자(Argument) │
└──────┸──────────────────────────┘
주의) 다음의 변수들을 unset 했을경우 (unset 명령을 사용해서)
로그인 세션동안 그들을 다시 reset한다 하더라도, 그들에게
부여됐던 특별한 의미는 삭제된다.
┌────────────┐
│ PPID , _ , RANDOM │
└────────────┘
다음은 Korn Shell 변수를 설명하기 위한 예이다.
┌──────────────────┐
│ KORN> print $RANDOM $RANDOM │ <--(1)
│ 15359 468 │
│ KORN> read │
│ welcome to korn │ <--(2)
│ KORN> echo $REPLY │
│ welcome to korn │
│ KORN> echo $_ │ <--(3)
│ korn │
│ KORN> pwd │
│ /home/mjr/bin │
│ KORN> dir=$PWD │ <--(4)
│ KORN> cd /etc │
│ KORN> cp passwd $dir/mycopy │
│ KORN> cp $OLDPWD/group . │
│ KORN> ls │
│ group mycopy │
└──────────────────┘
(1) RANDOM 변수의 처리 내용을 보여주고 있다. RANDOM 변수는 무작위의
숫자를 돌려주는 변수이다.
(2) REPLY 변수는 read 명령어를 통해 입력을 받아들였을때, 이를 저장하기
위해 사용하는 변수이다. 입력을 저장할 변수는 사용자 임의로도
지정 가능하다. 그 내용은 차후 설명된다.
(3) 사용자가 정의한 변수 dir에 PWD 변수에 설정되어 있는 값을
할당해서, 이를 다른 명령어 라인에서 참조하는 것을 보여주는 예이다.
3. 쉘 변수를 이용한 환경 제어
이전에 설명했던 변수들의 디폴트 값을 변경하거나 값을 설정함에 의해서,
사용자는 작업 환경의 형세를 변경할 수 있다. 이들 변수의 값을 변경하기
위해서는 variable=value의 형식으로 변경한다.
변수의 값이 설정했을때, 이 값을 서브 쉘에서 이용가능하게 하기 위해서는
변수를 반드시 하위쉘에 전달해 주어야 한다. 이것은 export variable_name
명령어를 통해 수행할 수 있다.
┌─────────────────────┐
│ $set │ 이 예는 set 명령어를 통해
│ HOME=/home/mjr │ 현재작업환경을구축을 위해
│ IFS= │ 설정되어 있는 변수들을
│ LOGNAME=mjr │ 확인하는 예이다.
│ MAIL=/usr/mail/mjr │
│ PATH=/bin:/usr/bin:/sbin:/etc:/usr/lib:.:│
│ PS1=$ │
│ PS2=> │
│ SHELL=/sbin/sh │
│ TERM=uvt1224 │
│ TERMCAP=/etc/termcap │
│ TZ=EST5EDT │
└─────────────────────┘
┌─────────────────────┐
│ $env │ 이 예는 env 명령어를 통해
│ HOME=/home/mjr │ export된변수들을확인하는
│ LOGNAME=mjr │ 예이다.
│ MAIL=/usr/mail/mjr │
│ PATH=/bin:/usr/bin:/sbin:/etc:/usr/lib:.:│
│ PS1=$ │
│ SHELL=/sbin/sh │
│ TERM=uvt1224 │
│ TERMCAP=/etc/termcap │
│ TZ=EST5EDT │
└─────────────────────┘
┌────────────────┐
│ $PS1="prompt> " │ 이 예는 변수를 서브 쉘에서
│ prompt> PS2="continue> " │ 참조할 수 있도록 export하는 방식과
│ prompt> echo this \ │ 실제 하위 쉘에서 참조되는 것을
│ continue> on the next line │ 보여주는 예제이다.
│ this │
│ on the next line │
│ prompt> export PS1 PS2 │
│ prompt> sh │
│ prompt> │
└────────────────┘
4. .profile
사용자가 시스템에 로그인을 수행하면, 쉘에 의해 수행되는 첫번째 화일이
/etc/profile이라는 화일이다. 이 화일은 일반적으로 시스템 관리자에 의해
유지관리된다. Bourne Shell과 Korn Shell 모두 이 화일을 사용한다.
이 화일은 보통 시스템 관리자가 모든 사용자에 대해 기본적으로 작업할 수
있는 환경을 구축하기 위한 변수들을 이 화일에 선언해둠으로써, 각 사용자의
기본적인 작업 환경을 구축해주는 용도로 사용된다.
각각의 사용자는 자신의 고유한 작업 환경을 구축하기 위해 .profile이라는
화일을 이용할 수 있다. ( 이 화일은 반드시 홈 디렉토리내에 위치되어
있어야만 유효하다는 것을 주목해야 한다. ) 사용자가 시스템에 로그인을
할때, /etc/profile이 실행되고, 그리고 쉘에 의해 .profile이 실행된다.
.profile은 /etc/profile이 실행된후 실행되어지는 이유로해서,
각각의 변수들은 .profile에 의해 변경될 수 있다.
.profile은 사용자가 로그인시에 실행하기를 원하는 명령어들을 실행하기
위해서도 사용될수 있다.
다음은 일반적인 .profile의 예이다.
┌─────────────────────┐
│ $HOME/.profile │
├─────────────────────┤
│ PS1="! $PWD> " │
│ PS2="continue> " │
│ PS3="choose> " │
│ PS4="trace> " │
│ PATH=/usr/bin:/bin:/etc:/usr/lib:: │
│ alias ls="ls -CF" │
│ alias id="who am i;id;pwd" │
│ HISTSIZE=20 │
│ HISTFILE=$HOME/.sh_history │
│ alias h=history │
│ TMOUT=600 │
│ export PS1 PS2 PS3 PS4 HISTSIZE │
│ export HISTFILE PATH │
│ export TMOUT │
│ banner HELLO $LOGNAME │
│ id │
└─────────────────────┘
---------------------------------------------------------------------------
Shell Program의 Comment 처리 기능
쉘 프로그램에서의 주석처리 방식
이후 몇번의 강의에 걸쳐서 Bourne Shell과 Korn Shell의 기본적인 기능들을
이용하여 쉘 프로그램에서, 문자열, 화일의 상태, 실행 결과의 상태와 숫자값을
다루는 방식에 대해 설명합니다.
1. 주석 (Comments)
주석은 쉘프로그램을 작성할때 매우 유용하다. 작성된 프로그램에 대해
프로그램내의 주요 부분에 설명 문구를 남겨놓음으로써, 차후에 쉽게 분석할수
있도록 하기위한 목적으로 사용된다.
적절한 주석은 프로그램들을 검증과 유지보수를 쉽게 해준다.
가장 일반적인 주석은 # 심볼이다. 프로그램 실행시 쉘이 #을 만나면,
그 라인의 나머지 부분을 주석문으로 처리한다.
주석을 나타내기 위해 사용할수 있는 다른 문자는 :(콜론)이다.
그러나 :은 라인의 처음에 기술해야 한다는 제한이 있다.
본래 :은 쉘의 null 문자로 사용되는 특수문자이다.
만일 :이 라인의 중간에 기술되어 있을 경우에는, 프로그램은 필드구별자로써
분석되지 않음으로써 문제가 발생할수도 있으니 주의해야 한다.
다음은 쉘 프로그램내에서 주석을 선언하는 방식을 보여주는 예제이다.
┌───────────────────────────────┐
│ # │<- (1)
│ #This program shows comments. │
│ # │
│ echo "This is it" # This line performs an echo. │<- (2)
│ :The colon can be used for comment at beginning of a line │<- (3)
└───────────────────────────────┘
이 예에서는 # 심볼을 사용하여 라인 전체를 주석처리하는 방법(1)과
라인의 임의의 위치 이후를 주석 처리하는 방식(2)을 보여주고있다.
또한 :을 사용하여 라인전체를 주석 처리하는 것(3)을 보여준다.
┌────────────────────┐
│ $ls -al | │
│ >#this is another use of a comment │
│ >pg │
└────────────────────┘
이 예는 표준 입력 처리시의 주석처리 방식을 보여준다.
---------------------------------------------------------------------------
쉘 프로그램에서의 변수의 운용 I
1. 변수( 키워드 파라메터 )
대부분의 모든 프로그래밍 언어들과 같이, 쉘 프로그램에서도 사용자가
선언한 변수에 임의의 값을 저장하는 방식을 지원한다.
그러나 대부분의 다른 프로그램 언어들에서는 변수에 저장할 값의 데이타형을
한정시키는데 반하여, Bourne Shell에서는 변수에 저장되어지는 데이타 형에
대한 제한하지 않고 운영할 수 있게 해준다. 사용자가 쉘 변수에 어떠한 값을
할당하더라도 기본적으로 문자열로써 쉘에 의해 해독되어 처리되기 때문이다.
다음은 Bourne/Korn Shell에서 동일하게 사용되는 변수 운용상의
규칙을 나타낸다.
┌──────────────────────────────┐
│ 1. 변수명은 반드시 영문자나 밑줄문자(_)로 시작해야 한다. │
│ 2. 변수명에는 영숫자와 밑줄문자(_) 만이 포함될수 있다. │
│ 3. 변수명은 최소 한자 이상이어야 한다. │
│ 4. 변수값의 설정은 다음 형식으로 수행한다. │
│ 변수명=변수값 │
│ 5. 위의 형식에서 = 표시 전후에 공백을 지정하면 않된다. │
└──────────────────────────────┘
비록 프로그램 작성시 필수 요소는 아니지만, Korn Shell은 Bourne Shell과
달리 변수에 저장될 데이타형을 한정하는 방식을 제공한다.
변수에 데이타형을 지정하기 위해서는 typeset 명령과 그의 옵션을 사용하여
수행한다.
다음은 typeset 명령어의 옵션들을 나열한 것이다.
-i[base] 정수형
-u 대문자
-l 소문자
-L[width] 왼쪽 정렬
-R[width] 오른쪽 정렬
-x export되는 변수
-r 읽기전용 변수
-Z 설정값 이외의 영역을 0으로 채운다.
-LZ 선행하는 0을 제거하고 왼쪽정렬한다.
-RZ 오른쪽 정렬을하고, 나머지 영역을 0으로 채운다.
typeset 명령어는 변수의 데이타형 속성의 제거및 변수값을 설정하기
위해서도 사용될 수 있다. 만약 변수에 속성이 설정되지 않으면,
Korn Shell은 변수의 값을 문자열로 간주한다.
C와 Pascal과 같은 프로그램 언어와 마찬가지로, 쉘 프로그램에서
사용할 변수는 사전에 선언되어야만 한다. Bourne Shell과 Korn Shell은
문자열로 처리하는 디폴트 변수형을 갖고 있음으로 인해, 변수는
이전에 참조함이 없이 설정될 수 있다. 쉘은 사용자가 변수를 사용하기를
원할때 값을 변수에 할당한다.
다음은 변수의 데이타형을 응용하는 예이다.
┌──────────────┬────────────────┐
│ Bourne/Korn │ Korn │
├──────────────┼────────────────┤
│$os=UNIX │ typeset -i sys=6000/55 │
│$sys=6000/55 │ typeset -u os=unix │
│$user=`who am i` │ typeset +u $os │<- (1)
└──────────────┴────────────────┘
이 예에서 (1)에서의 +u 옵션은 os라는 변수에 설정되어 있는 값을
소문자로 다시 변환하기 위한 옵션이다.
2. 변수에 설정된 값의 프로그램내에서의 참조 방식및표시 방식
변수는 특수문자인 $ 문자를 사용함으로써 참조할 수 있다.
변수가 참조될때 $ 문자의 뒤에 적당한 변수명이 지정되어 있으면,
쉘은 그 싯점에 해당 변수에 저장되어 있는 값으로 대치해준다.
echo 명령어(/bin/echo)는 화면상에 특정 변수에 설정되어 있는 값을 표시하기
위해서 사용할 수 있다.
현재 선언되어 있는 모든 변수및 설정값을 확인하기 위해서는 set과 env
명령어를 사용할 수 있는데, set 명령은 export 여부와는 관계없이 현재의
쉘에 선언되어 있는 모든 변수를 표시해주며, env 명령은 현재의 쉘에
선언되어 있는 변수중 export되어진 변수들만을 표시해준다.
Korn Shell에서는 변수의 값을 표시하기 위해 echo 명령어 이외에도,
print 명령어를 제공한다. 만약 쉘 처리 속도가 쉘 프로그램 작성시 중요한
요소일 경우에는, Korn Shell에서 실행되는 쉘 프로그램이면 echo 명령어를
사용하는 것보다 print 명령을 사용하는 것이 보다 효율적이다.
한가지 주목하기 바란다. 그것은 쉘은 항상 변수의 참조가 기술되어 있는
명령어 라인을 실행하기 전에변수의 대치가 수행된다는 것이다.
그러므로 변수로서 참조되는 명령어를 실행할 수 있게 된다.
다음은 변수의 참조및 변수값의 표시 방식을 보여주는 예이다.
┌────────────────┬────────────────┐
│ Bourne/Korn │ Korn │
├────────────────┼────────────────┤
│$os=UNIX │ KORN> typeset -i sys=6000/55 │
│$sys=6000/55 │ KORN> typeset -u os=unix │
│$user=`who am i` │ KORN> user=$(who am i) │
│$echo $os on the $sys │ KORN> print $os on the $sys │
│UNIX on the 6000/55 │ UNIX on the 109 │
│$cmd=cp │ KORN> print $user │
│$new_dir=/home/mjr/bin/ │ mjr term/03 Sep 12 8:35 │
│$echo cmd $cmd │ KORN> typeset +u $os │
│cmd cp │ KORN> echo $os │
│$echo new_dir $new_dir │ unix │
│new_dir /home/mjr/bin/ │ │
│$ls │ │
│f1 f2 f3 f4 │ │
│$echo $cmd * $new_dir │ │
│cp f1 f2 f3 f4 /home/mjr/bin/ │ │
│$$cmd * $new_dir < ━┓ │ │
│$ls $new_dir ┃ │ │
│f1 f2 f3 f4 ┃ │ │
└──────────── ┃ ──┴────────────────┘
┗━ (주목)
위의 예제의 (주목) 부분에 기술되어 있는 명령어 라인이 실행될때 수행되는
일련의 처리과정은 다음과 같다.
1. 쉘은 변수 대치를 수행하기 위해 명령어 라인을 탐색한다.
그럼으로써 $cmd를 대신해 cp를, $new_dir을 대신해 /home/mjr/bin/를
대치한다.
2. 쉘은 화일명 확장문자를 검색하기 위해 명령어 라인을 재탐색한다.
이로인해 명령어 라인에 기술되어 있는 *를 대신하여, 현재 디렉토리내의
모든 화일명을 대치시키게 된다.
3. 쉘은 변환된 명령어 라인을 명령어와 인자로 분석한다.
4. 쉘은 cp 명령어를 실행하고, 명령어 라인상에 기술되어 있던 인자들을
전달한다.
---------------------------------------------------------------------------
쉘 프로그램에서의 변수의 운용 II
1. Bourne Shell과 Korn Shell에서의 변수를 이용한 입력 받아들이기
쉘 프로그램에서는 read 명령어를 통해서 표준입력으로 사용자가
입력한 내용을 프로그램내의 변수에 저장할 수 있다.
read 명령어의 형식은 다음과 같다.
┌──────────────┐
│ read 변수명1 변수명2 │
└──────────────┘
이때 사용자가 지정한 여러개의 입력은(공백으로 구분되는 각각의 입력값)
지정 순서에따라 read 명령어에 지정된 각각의 변수에 별도로 저장되어진다.
통상 단말로부터 입력을 받아들이는 프로그램에서는 입력받을 내용을
알려주는 메세지를 스크린상에 표시한후 입력을 받아들인다. 그래서
read 명령어도 통상은 echo 명령어와 함께 지정하게 된다. 즉, 프로그램에서
echo 명령어를 사용하여 입력사항을 알려주는 메세지를 표시한후, read
명령어를 통해 입력을 받아들이는 형식이다.
이때 read 명령어는 echo 명령어에 의해 메세지가 표시되는 라인의
다음 라인에서 입력을 받아들이게 된다.
다음은 표준 입력으로부터 입력을 받아 처리하는 프로그램 예이다.
┌───────────────────────┐
│ myprog │
├───────────────────────┤
│ echo "Please enter the input filename │
│ followed by the output filename" │
│ read file1 file2 │
│ echo Input: $file1 │
│ echo Output: $file2 │
└───────────────────────┘
(실행예)
$myprog
Please enter the filename
followed by the output filename
data1 report1 <------ 사용자가 입력한 내용
Input: data1
Output: report1
만일 echo 메세지에 의해 표시되는
라인에서 연속하여 입력을 받아들이고 싶은 경우에는 echo 명령어에 의해
표시되는 메세지의 끝에 \c문자를 지정한다. 이 경우에 메세지가 표시된
라인에서 read 명령어가 입력을 받아들이게 된다.
다음은 메세지에 연이어 입력을 받아들이도록 지정된 예제 프로그램이다.
┌───────────────────────┐
│ myprog1 │
├───────────────────────┤
│ echo "Please enter the input filename │
│ followed by the output filename \c" │
│ read file1 file2 │
│ echo Input: $file1 │
│ echo Output: $file2 │
└───────────────────────┘
(실행예)
$myprog
Please enter the filename ┌───────┐
followed by the output filename │data1 report1 │
Input: data1 └───────┘
Output: report1 사용자의 입력이 메세지에 연이어
지정되어 있다.
2. Korn Shell 특유의 입력 방식
read 명령어는 디폴트로 표준입력으로부터 입력을 받아들이는 명령어임을
이미 설명했다.
만약 read 명령어에 변수가 지정되지 않았을 경우에는 어떻게 될까?
이 경우 read 명령어는 쉘 변수인 REPLY 변수에 입력을 할당한다.
주의할 것은 REPLY 변수는 Korn Shell에서만 지원하는 쉘 변수라는 것이다.
이때 입력된 라인의 각각의 필드는 IFS 변수에 선언되어 있는 문자에 의해
구분된다. 나열된 각각의 변수들에는 지정 순서대로, 입력된 필드들이
하나씩 할당된다.
그러나 read 명령어에 지정된 변수의 수가 입력 라인의 필드의
수보다 적을 경우에는 변수의 수를 넘어가는 필드들은 모두 마지막에 지정된
변수에 저장되어 진다. 또 이와 반대로 변수의 수가 입력 필드의 수보다 많을
경우에는 대치되고 남는 변수에는 NULL 값이 저장된다.
이전에 입력사항에 대한 메세지를 표시하기 위해서, echo와 read 명령어를
통상 함께 사용한다고 설명했다. 그러나 Korn Shell에서는 read 명령어에서
메세지를 함께 표시할수 있는 기능을 제공한다. 이는 read 명령어에 지정된
첫번째 변수명의 끝에 ?를 지정함으로써 가능하게 된다.
다음은 이를 보여주는 예이다.
┌─────────────────────────┐
│ myprog │
├─────────────────────────┤
│ read file1?"Please enter the input filename │
│ followed by the output filename" file2 │
│ echo Input:$file1 │
│ echo Outfile:$file2 │
└─────────────────────────┘
$myprog
Please enter the input filename
followed by the output filename
data1 report1
Input: data1
Output: report1
read 명령어는 -r이라는 옵션을 제공한다.
이 옵션은 newline 문자의 목적으로 라인의 끝에 \를 대신하기 위해서
사용된다.
┌─────────────────────────┐
│ myprog │
├─────────────────────────┤
│ read -r file1?"Please enter the input filename │
│ followed by the output filename" file2 │
│ echo Input:$file1 │
│ echo Outfile:$file2 │
└─────────────────────────┘
$myprog
Please enter the input filename
followed by the output filename data1 \
report1
Input: data1
Output:
3. 변수의 조합
쉘 프로그램을 작성할때에 간혹 특정한 명령어들을 실행하기 위해
변수값에 특정한 영숫자들을 덧붙이고자 하는 경우가 있을 것이다.
예를들면 어느 변수에 "myfile"이라는 문자열이 저장되어 있을때,
이 문자열의 끝에 "1"이라는 문자를 붙여, "myfile1"으로 변환하고자
하는 경우이다.
이런 작업은 { } 심볼 즉 브레이스 문자를 이용하면 행할수 있다.
대략적으로 이 기호를 사용하는 방법은 다음과 같다.
┌──────────────┐
│ ${변수명}추가문자열 │
└──────────────┘
이 형식에서 $는 변수의 참조를 의미하고, {} 기호 안에 기술되어 있는
변수명은 참조하고자 하는 문자열이다. 그리고 참조하는 변수명에 조합하고자
문자열을 연이어 기술한다.
다음은 변수의 조합에 관련된 예이다.
┌──────────────────┐ 이 예에서의 사용자의 의도는
│ $f=abcdefghijkl │ f 라는 변수의 값에 m이라는
│ $echo $f │ 문자열을 추가하여 표시하고자
│ abcdefghijkl │ 하는 것이다. 그래서 $fm이라고
│ $echo $fm │ 지정하였더니 아무것도 출력이
└──────────────────┘ 되지 않는다.
왜 그럴까? 이는 당연한 결과이다. 그 이유는 fm이라는 문자 자체를 쉘은
변수명으로 인식하기 때문이다. 바로 이럴때 이를 해결하는 방식이
아래의 예이다.
┌──────────────────┐ {} 심볼을 이용하니까 실제로
│ $echo ${f}m │ 사용자의 의도대로 작업이
│ abcdefghijklm │ 수행되는 것을 확인할 수 있다.
│ $new_dir=/USAGE2/NEWDIR/ │
│ $echo "${new_dir}sub1/sub2/" │
│ /USAGE2/NEWDIR/sub1/sub2/ │
└──────────────────┘
--------------------------------------------------------------------------
명령어 라인상에 지정된 인자 처리
쉘 프로그램내에서 프로그램을 호출할때 명령어와 함께 지정한
인자(Arguments)들을 참조하여 작업을 수행할 수 있다.
이러한 인자들을 참조하기위해서는 이미 응용과정에서 설명했던
포지셔널 파라메터를 이용한다.
1. 포지셔널 파라메터(Positional Parameter)
포지셔널 파라메터는 처리를 위해서 명령어 라인상의 인자를 쉘 프로그램내로
전달하기 위해서 사용한다.
변수와 마찬가지로, 포지셔널 파라메터들은 명령어 라인상의 기술순서를
나타내는 숫자에 $ 심볼을 붙여 정의된다. 그 형식은 다음과 같다.
Bourne Shell과 Korn Shell의 공통사항
$cmd arg1 arg2 arg3 arg4 arg5 arg6 arg7 arg8 arg9
│ │ │ │ │ │ │ │ │ │
│ │ │ │ │ │ │ │ │ │
$0 $1 $2 $3 $4 $5 $6 $7 $8 $9
이 형식에서 $0는 항상(?) 명령어를 참조하는 포지셔널 파라메터이다.
Korn Shell에서는 9개의 이상의 포지셔널 파라메터를 사용할 수 있다.
Bourne Shell과 마찬가지로, Korn Shell은 포지셔널 파라메터 지정시에
$심볼과 함께 오직 한자의 숫자만을 허용한다. 따라서 10번째 이상의
인자를 참조하기 위해서는 {}심볼을 사용하여 두자리 이상의 숫자를
묶어 주어야 한다. 다음은 Korn Shell에서 인자를 참조하기 위해
포지셔널 파라메터를 지정하는 방식을 보여준다.
Korn Shell의 특유 지정형식
$cmd arg1 arg2 arg3 arg4 arg5 arg6 arg7 arg8 arg9 arg10 arg11 ..
│ │ │ │ │ │ │ │ │ │ │ │
│ │ │ │ │ │ │ │ │ │ │ │
$0 $1 $2 $3 $4 $5 $6 $7 $8 $9 ${10} ${11}
다음은 포지셔널 파라메터를 사용하는 예제이다.
┌─────────────────────┐
│ $cat frmat │
│ pr -d -o5 $1 | lp │
│ $frmat newfile │
│ request id is lpt-101 (standard input) │
└─────────────────────┘
이 예제에서 frmat라는 쉘 프로그램은 프로그램을 기동할때 사용자가
인자로서 제공하는 화일을 프린트 형식으로 변환하여, 프린트를 수행하도록
하는 간단한 쉘 프로그램이다. 이때 쉘 프로그램에서 보면, 사용자가
프로그램 호출시 함께 제공하는 인자를 참조하기 위해서 $1 이라는
포지셔널 파라메터가 지정된 것을 볼수있다. 그리고 쉘 프로그램 실행시
인자를 지정하는 방식을 보여주며, 쉘 프로그램을 실행하고 있다.
즉; 위 예제에서 쉘 프로그램내의 포지셔널 파라메터인 $1은 사용자가 입력한
인자인 newfile로 대치되어 실행되게 된다.
비록 Bourne Shell에서 9개의 포지셔널 파라메터 밖에 제공하지 않는다고
해서, 오직 9개의 인자만을 프로그램에서 처리할 수 있다는 것은 아니다.
다음에 설명되는 shift 명령어를 통해 Bourne Shell에서도 9개 이상의
인자들을 프로그램에서 참조할 수 있다.
2. shift 명령어
shift 명령어는 Bourne Shell과 Korn Shell에서 포지셔널 파라메터를
왼쪽으로 이동하기 위해 사용하는 명령이다.
이는 shift 명령을 수행하면, 기존에 포지셔널 파라메터 $2에 저장되어 있는
인자가 포지셔널 파라메터 $1에 저장되어지고, $2에는 $3에 저장되어 있던
내용이 저장되어짐을 의미한다. 이렇게 되면 명령어 실행시 10번째 지정되어
있던 인자가 $9에 저장되어지기 때문에, 사용자는 9개 이상의 인자를
프로그램에서 참조할 수 있게 된다.
주의) shift 명령어가 실행되면, 포지셔널 파라메터에 이전에 설정되었던
값은 다시 참조가 불가능하게 된다. 위의 설명에서 $1에 처음
설정되어 있던 값이 이에 해당한다. 만약 프로그램에서 이 값을
shift 명령 이후에 참조가 필요할 경우에는 shift 명령을 실행하기
전에 변수에 값을 저장해두어야만 한다.
물론 shift 명령에서는 한번에 여러개의 포지셔널 파라메터를 shift할 수
있다. 이를 위해서는 shift 명령어의 인자로써 이동 횟수를 지정한다.
그 형식은 다음과 같다.
┌────────────┐
│ shift 이동횟수 │
└────────────┘
다음은 shift의 사용 형식을 보여주는 예제 쉘 프로그램이다.
┌────────────┐ ( 실행 예 )
│ prog │ $prog a b c d e f g h i
├────────────┤ a b cd
│ echo $1 $2 $3 $4 │ b c d e
│ shift │ f g h i
│ echo $1 $2 $3 $4 │
│ shift 4 │
│ echo $1 $2 $3 $4 │
└────────────┘
위의 실행예가 나오게 되는 과정을 자세히 분석해보면 다음과 같다.
┌──────────────┰─┬─┬─┬─┬─┬─┬─┬─┬─┐
│ ┃$1│$2│$3│$4│$5│$6│$7│$8│$9│
┝━━━━━━━━━━━━━━╉─┼─┼─┼─┼─┼─┼─┼─┼─┤
│사용자가 입력한 상태 ┃a │b │c │d │e │f │g │h │i │
├──────────────╂─┼─┼─┼─┼─┼─┼─┼─┼─┤
│shift 명령 실행후의 상태 ┃b │c │d │e │f │g │h │i │ │
├──────────────╂─┼─┼─┼─┼─┼─┼─┼─┼─┤
│shift 4 명령 실행후의 상태 ┃f │g │h │i │ │ │ │ │ │
└──────────────┸─┴─┴─┴─┴─┴─┴─┴─┴─┘
이때 주목할 것은 shift되어 포지셔널 파라메터에
없어진 값들은 위의 주의 사항에서 설명 했듯이
다시 참조가 불가능하다는 것이다. 위의 예에서는
그 값들을 사전에 변수에 저장하지 않았기 때문.
프로그램에서 set 명령을 통해 포지셔널 파라메터에 임의 값을 재설정할수도
있다. 다음은 set 명령어에 대한 설명이다.
3. set 명령어
set 명령어는 쉘로 전달된 인자들을 설정및 재설정하기 위해 사용된다.
통상은 set 명령어는 shift 실행후 참조가 불가능하게 된 인자를 다시
참조하기 위해, 포지셔널 파라메터를 재구축하기 위해서 사용한다.
set 명령어에서 --라는 옵션을 사용할 수 있는데, 이 옵션은 인자들로부터
옵션을 구별하기 위해서 사용한다.
다음은 set 명령어를 사용하는 예 이다.
┌───────────────┐
│ proga │
├───────────────┤
│ echo $1 $2 $3 $4 │
│ set -- the new arguments │
│ echo $1 $2 │
│ shift 2 │
│ echo $1 │
└───────────────┘
$proga a b c d 이 예에서는 명령어 기동시 사용자가
a b c d 지정한 인자들을 포지셔널 파라메터를
the new 사용하여 참조한후, 프로그램내에서
arguments set 명령을 이용하여 포지셔널 파라메터에
새로운 값을 설정하는 것을 보여주고
있다.
┌───────────────┐
│ progb │
├───────────────┤
│ echo $1; p1=s1 │
│ shift │
│ echo $1; p2=$1 │
│ set -- $p1 $p2 │
│ echo $1 $2 │
└───────────────┘
$progb a b 이 예에서는 shift 명령의 실행 결과
a 참조가 불가능하게 되는 인자를
b shift 명령 실행후에도 참조할 수 있도록
a b 하기위해 그 값을 변수에 저장해둔후,
필요시 set 명령을 통해 포지셔널 파라메터에
재설정 운영하는 방식을 보여주는 예이다.
'Academy I > Tech Academy' 카테고리의 다른 글
Unix 13 쉘 프로그램의 제어문 (0) | 2014.12.04 |
---|---|
Unix 12 Shell Program Debugging I (0) | 2014.12.04 |
Unix 11 Regular Expression과 Filters: sort, tr (0) | 2014.12.04 |
Unix 10 Shell Program에서 쉘 특수 변수의 이용 (0) | 2014.12.04 |
Unix 8 쉘프로그래밍 (0) | 2014.12.04 |
Unix 7 프로세스, 쉘 (0) | 2014.12.04 |
Unix 6 유틸리티 (0) | 2014.12.04 |
Unix 5 통신,네트워크 (0) | 2014.12.04 |