본문 바로가기

Academy I/Tech Academy

Unix 10 Shell Program에서 쉘 특수 변수의 이용

Shell Program에서 쉘 특수 변수의 이용

1. Shell Special Variables(Parameters)의 참조

이전 강의에서 쉘 특수변수에 관해서는 이미 설명됐다.
쉘 특수변수들은 쉘 프로그램에서 요긴하게 사용할수 있다.
이러한 쉘 특수변수들은 사용자에게 특정 정보를 알려주기 위해서,
쉘에 의해 설정된다. 그러나 이들 변수는 쉘에의해 운영되어지는
변수이기 때문에, 사용자에 의해서 재정의 또는 설정할 수 없다.

다음은 쉘 특수 변수들이다.

┌────┰─────────────────────────────┐
│ 변수   ┃ 목적                                                     │
├────╂─────────────────────────────┤
│ $0     ┃명령어 라인상의 명령어 이름이 설정                        │
├────╂─────────────────────────────┤
│ $#     ┃명령어 라인상에 기술되어 있는 인자의 갯수가 설정          │
├────╂─────────────────────────────┤
│ $*     ┃명령어 라인내의 전체 인자가 설정                          │
├────╂─────────────────────────────┤
│ "$@"   ┃$*와 같다. 그러나따옴표로 둘러쌓인 인자를 하나의 인자로   │
│        ┃처리하는 것이 $*와 틀린 점이다.                           │
├────╂─────────────────────────────┤
│ $$     ┃현재의 프로세스 식별자(PID)가 설정                        │
├────╂─────────────────────────────┤
│ $!     ┃Background에서 직전에 실행됐던 프로세스의 식별자가 설정   │
├────╂─────────────────────────────┤
│ $?     ┃직전에 Foreground에서 실행됐던 명령어의 종료상태가설정    │
├────╂─────────────────────────────┤
│ $-     ┃현재 설정된 쉘 옵션이 설정                                │
└────┸─────────────────────────────┘

. $* 변수에는 포지셔널 파라메터처럼 참조하는 인자의 갯수에 대한
제한이 없다.

. $@ 변수에 쌍따옴표를 지정하지 않을 경우에는 $*와 완전히 동일하게
수행된다.

. $-에 참조되는 쉘 옵션들은 set 명령어로 설정된 쉘 옵션들이다.

. shift 명령어가 실행되면 $# 변수의 값은 자동적으로 줄어든다.

다음은 이들 변수의 참조 결과를 보여주는 예제이다.

┌────────────┐ ( 실행예 )
│ Special │ $special a b c
┝━━━━━━━━━━━━┥┌─────── a b c
│ pos=$*; echo $pos ──┼┘┌────── 3
│ num=$#; echo $num ──┼─┘┌───── a
│ echo $1; shift ──┼──┘┌──── 2
│ echo $# ─────┼───┘┌─── special
│ echo $0 ──────┼────┘┌── b
│ echo $1 ─────┼─────┘┌─ 3
│ set -- $pos │ │ a b c
│ echo $# ────┼──────┘ │
│ echo $1 $2 $3 ─┼─────────┘
└────────────┘

이 예제는 독자 스스로한번 분석해 보기를 부탁한다.

2. 서브 쉘로의 변수 전달

서브 쉘로의 변수 전달은 부모 쉘로부터 변수를 export함에 의해
행할 수 있다. export된 변수들은 그들이 unset 또는 reset될때까지
쉘에서 유지된다.

명령어 라인상에서 서브 쉘로 변수를 export한다고 해서, 프로세스가
종료되어도 변수가 존재한다는 것은 아니다. 변수는 변수를 선언한 프로세스
가 존재하는 동안에만 존재하고, 해당 프로세스가 종료하면 변수도 삭제되게
된다.

다음은 이에 관련된 예제이다.

┌──────────────────────────┐
│ $cat cmd1 │
│ user3=C │
│ echo x=$x │
│ echo user1=$user1" "user2=$user2" "user3=$user3 │
│ $x=local │
│ $user1=A user2=B cmd1 │<-- (1)
│ x= │
│ user1=A user2=B user3=C │
│ $echo user1=$user1 user2=$user2 │<-- (2)
│ user1= user2= │
│ $val=value echo $val │<-- (3)
│ │
│ $ │
└──────────────────────────┘

(1)의 명령어 라인 실행 결과를 주의깊게 살펴봐야 한다.
우선 cmd1이라는 쉘 프로그램을 살펴보자. 이 프로그램에서는
user3라는 변수에 C라는 값을 대입하여 설정한후, x라는 변수의 내용을
echo 명령어를 통해 표시하고, 또한번의 echo 명령을 통해 user1,
user2, user3 변수의 내용을 표시하고 있다.
그런데 프로그램에서는 오직 user3라는 변수에 대한 선언만을 행하고
있다. 그래서 프로그램을 실행하기 전에 나머지 변수들을 선언했다.

이 예에서는 변수의 선언을 두가지 방식으로 행하고 있다.
x라는 변수는 프로그램 실행을 요구한 명령어 라인과 별도의 라인에
선언하였고, user1과 user2 변수는 동일라인에서 선언했다.

그런데 프로그램을 실행한후, 결과에서 보면은 x 변수의 값이 표시되지
않는 것을 볼수 있다. 왜일까? 그것은 x라는 변수는 다른 명령어 라인에
기술되었기 때문에 하위 쉘에서 수행되는 쉘 프로그램에서는 x 변수를
참조할 수 없었기 때문이다. 즉; export되지 않았기 때문이다.
그런데 user1과 user2는 참조되고 있다. 그것은 두 변수 선언 자체도
서브 쉘에서 수행되었기 때문이다.

서브쉘에서 선언되었다는 것은, (2)에 지정되어 있는 명령어의
실행 결과에서 확인할 수 있다. echo 명령어를 통해 두 변수의 내용을
확인해보니 미정의된 것으로 처리되는 것을 볼수 있다. 변수는 프로세스가
존재하는 동안에만 존재하기 때문에, 프로그램 실행이 끝나고 프로세스가
종료되자, 자동적으로 변수도 삭제되었기 때문이다.

그런데 (3)에서 지정된 명령어 라인에서는 앞과 다르게 처리되고 있다.
(1)에서와 동일한 방식으로 변수를 선언하고 echo 명령어를 실행했는데,
echo 명령어에서val 변수를 참조하지 못하고 있다. 이것은 UNIX의
Built-in 명령어는 현재의 쉘에서 실행되는데, 이때 동일 명령어 라인상에
선언되어 있는 변수는 참조가 불가능하기 때문이다.

이를통해 변수의 존재기간및 서브쉘로의 export 필요성을 알수 있다.

3. 보호문자의 사용(Quoting)

특수문자에 대한 쉘의 처리를 금지하기 위해 사용하는 보호문자에는
기본적으로 다음과 같은 4가지가 있다. 이 보호문자에 대한 설명은
이미 응용과정및 쉘 프로그래밍과정의 처음에 설명했다. 따라서
이번 과정에서는 대략적으로 설명한다.

1) 따옴표 (')

따옴표로 둘러 쌓여 기술된 모든 특수문자들은 쉘로부터의 번역이
모두 금지된다. 따옴표는 특수문자의 기능을 필요에 의해 제한하기
위해서 사용한다.

2) 쌍따옴표 (")

쌍따옴표는 따옴표와 동일한 기능을 수행한다. 단, $, `, \의 3가지
특수문자는 영향을 받지않고 정상대로 쉘에의해 번역된다.

3) 역따옴표 (`)

역따옴표에 기술되어 있는 문자열을 쉘에게 실행 명령어라는 것을
알려주기 위해 사용한다. 쉘은 이 명령어를 실행한후 생성된 출력을
명령어가 지정된 위치에 위치시킨다.

( 주의 ) 이상 3가지의 보호문자는 반드시 쌍으로 이루어 진다.
만일 쌍으로 지정되지 않은 경우에는 문법 에러를 발생한다.

4) 역슬레쉬 (\)

역슬레쉬는 연이어 기술된 다음 한자가 갖고 있는 특수문자의
처리를 쉘이 수행하지 못하도록 처리한다.

다음은 보호문자를 처리시의 특이사항들을 나열한 것이다.

보호문자 사이에 지정되어 있는 필드 구별자들은(IFS에 지정되어 있는)
쉘에 의해 공백으로 대치되지 않고 그대로 유지된다.

Korn Shell에서 Alias와 기존의 명령어가 이름이 동일할 경우, 이를
지정할 경우에 Alias가 우선 실행된다. 이때 동일 이름의 명령어를
실행하기 위해서는, 명령어 이름에 보호문자를 지정함으로써 실행
가능하다.

┌─────────────────┰─────────────────┐
│ Bourne/Korn ┃ Korn │
┝━━━━━━━━━━━━━━━━━╋━━━━━━━━━━━━━━━━━┥
│ $echo '* $PATH' ┃ KORN>alias ls=`ls -CF` │
│ * $PATH ┃ KORN>ls │
│ $echo "* $PATH" ┃ bin/ f1 f2 f3 f4 m1* m2* m3* m4* │
│ * :/etc:/usr/local/bin:/usr/bin ┃ KORN>\ls │
│ $echo \* \$PATH ┃ bin f1 f2 f3 f4 m1 m2 m3 m4 │
│ * $PATH ┃ KORN>"ls" │
│ $current=`pwd` ┃ bin f1 f2 f3 f4 m1 m2 m3 m4 │
│ $cd /etc ┃ │
│ $echo "$LOGNAME is in" `pwd` ┃ │
│ mjr is in /etc ┃ │
│ $cd $current ┃ │
│ $pwd ┃ │
│ /home/mjr ┃ │
└─────────────────┸─────────────────┘

이 예제는 독자 스스로 분석해 보기를 부탁한다.


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


Shell Program의 명령어 실행상태 검증

Shell Program의 종료상태 검증

1. 쉘 프로그램의 종료 상태

프로그램이 실행 완료되었을때는 언제나, 시스템에 종료상태에 대한
정보를 되돌려준다. 이때의 종료상태 정보는 통상 프로그램이 성공적으로
실행되었는지 아니면 그렇지 않은지를 나타내는 숫자로 표시된다.
관례상 종료상태 0은 성공적인 실행을 의미하며, 0이 아닌 다른 값은
실행이 실패했음을 의미한다.

( 주의 ) 유닉스에서의 종료상태를 나타내는 0과 0이 아닌다른 값의
의미는 C 언어에서의 종료상태 표시와 반대이다.
C 언어에서는 0은 참을 0이 아닌값은 거짓을 의미한다.
쉘 프로그램을 작성할때 주의하기 바란다.

프로그램에서 종료상태를 이용하는 것은 명령어가 성공적으로 실행되었는지
확인하기 위해, 또한 에러 처리와 프로그램 진행에 있어서 프로그램 제어를
위해 매우 유용하게 이용할 수 있다.

이미 설명했던 쉘 특수 변수중에서 $?를 사용하여 사용자는 직전에
Foreground에서 실행된 프로그램의 종료 상태를 확인할 수 있다.

( 예제 )
┌──────────────────────┐
│ $ls -CF │
│ bin/ f1 f2 f3 f4 m1* m2* m3* m4* │
│ $echo $? < ────┼── 직전의 ls
│ 0 │ 명령이 성공적
│ $ls -z │ 으로 실행됐다.
│ ls: illegal option -- z │
│ usage:ls -1RadcmnlogrtucpFbqisfl [files] │
│ $echo $? < ──────────┼── 직전의 ls
│ 2 │ 명령이 실패했다.
└──────────────────────┘

2. exit 명령어

사용자는 exit 명령어를 사용하여 프로그램을 종료할 수 있는데,
이때 사용자 임의의 종료상태 정보를 설정할 수 있다.

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

┌───────┐
│ exit n │
└───────┘

사용자는 exit 명령어를 사용하여 프로그램을 종료할 수 있다.
exit 명령어에 의해 종료되어진 프로그램으로 부터의 종료상태는,
exit 명령에 지정된 숫자로 돌려주거나, 만일 exit 명령어에 n을 지정하지
않았을 경우에는 마지막 명령의 종료상태가 돌려진다.

exit 명령어는 프로그램을 분기했을때 유용하게 사용할 수 있다.

( 예제 1 )
┌───────────────────────┐
│ myprog │
├───────────────────────┤
│ echo "Please enter the input filename │
│ followed by the output filename \c" │
│ read file1 file2 │
│ echoInput: $file1 │
│ echo Output: $file2 │
│ exit 5 │
└───────────────────────┘
$myprog
Please enter the input filename
followed by the output filename data1 report1
Input: data1
Output: report1
$echo $?
5 <--------------- 프로그램에서 프로그램을 종료하며, 5라는
종료상태 정보를 남긴 결과이다. 이 예에서
0이 아닌 다른 값의 종료상태 정보가 표시
되었다고 해서, 실행 실패라는 의미로 해석
하면 않된다. 이 예에서는 단지 사용자가
프로그램 실행종료시 임의의 종료상태를
남길수 있다는 것을 보여주기 위한 것이다.

( 예제 2 )
┌───────────────────────┐
│ myprog │
├───────────────────────┤
│ echo "Please enter the input filename │
│ followed by the output filename \c" │
│ read file1 file2 │
│ echo Input: $file1 │
│ echo Output: $file2 │
│ exit │
└───────────────────────┘
$myprog
Please enter the input filename
followed by the output filename data1 report1
Input: data1
Output: report1
$echo $?
0 <------------------ 위의 프로그램에서 exit 명령을
선언시 숫자를 지정하지 않았다.
이 경우 앞서 설명한 것처럼 마지막
으로 실행한 명령의 실행 결과가
돌려진다.


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


Shell Program의 조건 검사 기능 I

Shell Program에서의 조건 검사 I

1. test 명령어

사용자는 쉘 프로그램내에서 test 명령문을 사용하여 숫자값, 화일종류
그리고 문자열을 검사할 수 있다. test 명령문은 검사되어야할 항목의
연산식(__EXPRESSION__)을 구성하기 위해서 여러가지 연산자등을 사용한다.
test 명령문의 실행 결과는 참일 경우 0 또는 거짓일 경우에는 0이 아닌
다른값으로 돌려진다.

Bourne Shell과 Korn Shell에서 "test __EXPRESSION__" 또는 "[ __EXPRESSION__ ]"
형식으로 검사구문을 지정하는 것을 허용한다. 이때 __EXPRESSION__은
검사해야할 대상이 무엇인가에 따라서 다양한 연산자들을 사용하여
기술할 수 있다.

__EXPRESSION__을 기술하때 반드시 전후에 공백을 지정해야만 한다.
예를들어 [__EXPRESSION__ ]과 같이 __EXPRESSION__ 전후에 공백을 지정해야 한다.

또한 __EXPRESSION__을 기술함에 있어 공백이 검사 대상으로 지정되어 있을
경우에는 반드시 보호문자로 둘러 쌓아야만 한다.
예를들어 [ cat = " cat" ]의 형식이다. 이 결과는 거짓이다. 이유는
" cat" 지정에는 공백이 들어있기 때문이다.

또한 복수개의 명령어 그룹화는 보호된 둥근괄호를 사용하여 test 명령문내
에서 행할 수 있다. 예를들면 [ ! \( -w f1 -a -x f1 \) ]와 같다.

Korn Shell은 [[ __EXPRESSION__ ]] 형식의 또다른 검사구문을 지원한다.
이 형식에서는 { }내에서 단어 분할 또는 경로명 확장 지정을 허용하지
않는다. __EXPRESSION__에서 논리곱(&&)과 논리합(||)등의 연산자들의 사용및
명령어 그룹화를 위해 보호되지 않은 둥근괄호의 사용을 허용한다.
예를들면 [[ ! ( -w f1 && -x f1 ) ]]와 같다.

2. test 명령문의 논리 연산자

test 명령어에는 사용자가 복수개의 조건들을 조합하여 사용할 수 있도록
다음과 같은 논리 연산자들을 제공한다.

Bourne Shell과 Korn Shell의 공통 연산자

┌───────┰───────┐
│ 연산자 ┃ 의미 │
┝━━━━━━━╋━━━━━━━┥
│ ! ┃ 부정 │
├───────╂───────┤
│ -a ┃ AND │
├───────╂───────┤
│ -o ┃ OR │
└───────┸───────┘

KORN Shell 전용 연산자

┌───────┰───────┐
│ 연산자 ┃ 의미 │
┝━━━━━━━╋━━━━━━━┥
│ ! ┃ 부정 │
├───────╂───────┤
│ && ┃ AND │
├───────╂───────┤
│ || ┃ OR │
└───────┸───────┘

3. 화일종류 검사 연산자

사용자가 쉘 프로그램내에서 화일에 대한 다양한 형식의 검사를 수행할 수
있도록, 다양한 화일종류 검사를 실행하는 연산식(__EXPRESSION__)을 제공한다.

( 주의 ) 대개 디렉토리를 화일과 다른 요소로 생각하는 사용자가
많은데, 디렉토리도 정확하게 화일의 한 종류임을 혼돈하지
말기 바란다.

화일 검사가 수행될때, 화일 검사를 실행하고 있는 사용자를 기준으로한
화일에대한 접근권한 검사가 수행된다. 예를들면, 만일 검사를 수행하고
있는 사용자가 화일의 소유자이면, 쓰기권한에 대한 검사를 실행할 경우
오직 소유자에 부여되어 있는 쓰기권한에 대해서만 검사되고, 다른 사용자에
대한 쓰기권한은 검사되지 않는다.

화일 검사를 실행할 수 있는 연산식에는 다음과 같은 것들이 있다.

┌────────┰─────────────────────────┐
│ 연산식 ┃ 검사 내용 │
┝━━━━━━━━╋━━━━━━━━━━━━━━━━━━━━━━━━━┥
│-s file ┃화일이 존재하고, 길이가 0 보다 크면 참이다. │
├────────╂─────────────────────────┤
│-f file ┃화일이 존재하고, 일반 화일이면 참이다. │
├────────╂─────────────────────────┤
│-d file ┃화일이 존재하고, 디렉토리이면 참이다. │
├────────╂─────────────────────────┤
│-r file ┃화일이 존재하고, 프로세스에의해 읽기 가능하면 참. │
├────────╂─────────────────────────┤
│-w file ┃화일이 존재하고, 프로세스에의해 쓰기 가능하면 참. │
├────────╂─────────────────────────┤
│-x file ┃화일이 존재하고, 프로세스에의해 실행 가능하면 참. │
├────────╂─────────────────────────┤
│-c file ┃화일이 존재하고, 문자 장치 화일이면 참이다. │
├────────╂─────────────────────────┤
│-b file ┃화일이 존재하고, 블럭 장치 화일이면 참이다. │
├────────╂─────────────────────────┤
│-p file ┃화일이 존재하고, 파이프(Named Pipe)이면 참이다. │
├────────╂─────────────────────────┤
│-u file ┃화일이 존재하고, set-user-id bit가 설정되어 있으면│
│ ┃참이다. │
├────────╂─────────────────────────┤
│-g file ┃화일이 존재하고, set-group-id bit가 설정되어 │
│ ┃있으면 참이다. │
├────────╂─────────────────────────┤
│-k file ┃화일이 존재하고, sticky bit가 설정되어 있으면 │
│ ┃참이다. │
├────────╂─────────────────────────┤
│-L file ┃화일이 존재하고, 심볼릭 링크된 화일이면 참이다. │
├────────╂─────────────────────────┤
│file1 -nt file2 ┃file1이 file2보다 최신 화일이면 참이다. │
├────────╂─────────────────────────┤
│file1 -ot file2 ┃file1이 file2보다 오래된 화일이면 참이다. │
├────────╂─────────────────────────┤
│file1 -ef file2 ┃file1이 file2에 대한 다른 이름이면 참이다. │
└────────┸─────────────────────────┘


이상의 연산식을 사용하여 화일 검사를 수행하면, 조건이 참일 경우에는
0 의 값을, 거짓일 경우에는 0 이 아닌 다른 값을 돌려준다.

다음은 화일검사를 실행하는 예제를 들기위한 전제사항이다.

$who am i; id
mjr term/11 Oct 20 14:09
uid=300(mjr) gid=1 (other)
$ls -ld /home/mjr
drwxr-xr-x 7 mjr other 512 Aug 20 14:38 /home/mjr
$ls -lLd /etc/passwd /bin f1 m1
drwxrwxrwx 1 bin bin 8 Oct 11 15:54 /bin
-r--r--r-- 1 root sys 1472 Oct 19 08:10 /etc/passwd
-r--r--r-- 1 mjr other 14 Sep 16 10:58 f1
-r-xr-xr-x 1 mjr other 14 Oct 19 13:18 m1


( 예제 )

┌─────────────────┰─────────────────┐
│ Bourne/Korn ┃ Korn │
├─────────────────╂─────────────────┤
│$test -d /home/mjr ┃KORN> [[ -d /home/mjr ]] │
│$echo $? ┃KORN> print $? │
│0 ┃0 │
│$[ -d /home/mjr/f1 ] ┃KORN> [[ -d /home/mjr/f1 ]] │
│$echo $? ┃KORN> echo $? │
│1 ┃1 │
│$[ -w /etc/passwd ] ┃KORN> [ -w /etc/passwd ] │
│$echo $? ┃KORN> echo $? │
│1 ┃1 │
│$test -w /home/mjr -a -d /bin ┃KORN> test -w /home/mjr -a -d /bin│
│$echo $? ┃KORN> echo $? │
│0 ┃0 │
│$[ -w f1 -o ! -x m1 ] ┃KORN> [[ -w f1 || ! -x m1 ]] │
│$echo $? ┃KORN> echo $? │
│1 ┃1 │
│$[ !\( -w f1 -a -x f1 \) ] ┃KORN> [[ !( -w f1 && -x f1) ]] │
│$echo $? ┃KORN> echo $? ┃ │
│0 ┃0 ┃ │
└─────────────────┸────────── ┃ ─────┘

위의 예는 독자 스스로 한번 분석해보기를 바란다. ┃


둥근괄호를 보호문자를
사용하여 보호하지 않고
있다. Korn Shell에서는
지정할 필요가 없다.

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



Shell Program에서의 조건 검사 II

4. 숫자값 검사(value testing)

지정한 두개의 숫자에 대해 수학적인 조건 검사를 수행하는 연산식의
형식은 다음과 같다.

┌──────────────┰────────────────────┐
│ 연산식 ┃ 검사내용 │
┝━━━━━━━━━━━━━━╋━━━━━━━━━━━━━━━━━━━━┥
│ integer1 -eq integer2 ┃두 정수가 같으면 참 │
├──────────────╂────────────────────┤
│ integer1 -ge integer2 ┃integer1이 integer2보다 크거나 같으면 참│
├──────────────╂────────────────────┤
│ integer1 -gt integer2 ┃integer1이 integer2보다 크면 참 │
├──────────────╂────────────────────┤
│ integer1 -le integer2 ┃integer1이 integer2보다 적거나 같으면 참│
├──────────────╂────────────────────┤
│ integer1 -lt integer2 ┃integer1이 integer2보다 적으면 참 │
├──────────────╂────────────────────┤
│ integer1 -ne integer2 ┃두 정수가 다르면 참 │
└──────────────┸────────────────────┘

다음은 숫자값 검사를 수행하는 예이다.

┌────────────────┰────────────────┐
│ Bourne/Korn ┃ Korn │
├────────────────╂────────────────┤
│$test 5 -eq 5 ┃ KORN> [[ 5 -eq 5 ]] │
│$echo $? ┃ KORN> echo $? │
│0 ┃ 0 │
│$[ 5 -ge 5 ] ┃ KORN> [ 5 -ge 5 ] │
│$echo $? ┃ KORN> echo $? │
│0 ┃ 0 │
│$[ 5 -ge 6 -o 6 -lt 3 ] ┃ KORN> [[ 5 -ge 6 || 6 -lt 3 ]] │
│$echo $? ┃ KORN> echo $? │
│1 ┃ 1 │
└────────────────┸────────────────┘

위의 결과에서 $?의 결과가 0인 것은 조건이 참임을 의미하며,
0이 아닌 값은 조건이 거짓임을 의미한다.

5. 문자열 검사(String testing)

두개의 문자열의 내용을 비교하기 위한 연산식에는 두가지 종류가 있다.
즉 = 과 != 두개가 있다. 또한 문자열내의 문자의 존재 여부를 검사할
수도있다.

변수의 내용으로 null값이 들어 있을수 있는데, 이때 이 null값을
검사할때 에러가 발생하지 않도록 변수를 쌍따옴표(")로 묶어주는 것이
최선이다. 그리고 공백이 들어있는 변수의 내용 또는 공백을 검사 대상으로
할 경우에는 반드시 쌍따옴표(")로 묶어준다.

문자열 검사를 수행하는 연산식에는 다음과 같은 것이 있다.

┌─────────┰─────────────────────────┐
│ 연산식 ┃ 검사내용 │
┝━━━━━━━━━╋━━━━━━━━━━━━━━━━━━━━━━━━━┥
│string1 = string2 ┃두 문자열이 동일한 경우에 참 │
├─────────╂─────────────────────────┤
│string1 != string2┃두 문자열이 틀린 경우에 참 │
├─────────╂─────────────────────────┤
│string ┃지정한 문자열이 null이 아니면 참 │
├─────────╂─────────────────────────┤
│-z string ┃지정한 문자열이 0의 길이를 갖으면 참 │
├─────────╂─────────────────────────┤
│-n string ┃지정한 문자열이 0이 아닌 길이를 갖으면 참 │
└─────────┸─────────────────────────┘

2. Korn Shell에서 제공하는 추가의 문자열 검사 연산식

Korn Shell에서의 [[ , ]] 의 연산자와 함께, >(크다), <(적다)등의
연산자를 사용하여 Ascii 코드 값을 기준으로한 문자열의 검사를 수행할 수
있다.

다음은 이 두 연산식에 대한 설명과 그에 대한 예제이다.

┌─────────┰─────────────────────────┐
│ 연산식 ┃ 검사내용 │
┝━━━━━━━━━╋━━━━━━━━━━━━━━━━━━━━━━━━━┥
│string1 > string2 ┃ASCII 코드를 기준으로 string1이 string2보다 │
│ ┃빠르면 참이다. │
├─────────╂─────────────────────────┤
│string1 < string2 ┃ASCII 코드를 기준으로 string1이 string2보다 │
│ ┃느리면 참이다. │
└─────────┸─────────────────────────┘

┌────────────────┰─────────────────┐
│ Bourne/Korn ┃ Korn │
┝━━━━━━━━━━━━━━━━╋━━━━━━━━━━━━━━━━━┥
│$c="cat " ┃KORN>c=" cat " │
│$test $c = cat <---(1) ┃KORN>[[ cat > $c ]] │
│$echo $? ┃KORN>echo $? │
│0 ┃1 │
│$test "cat" ┃KORN>test $c = "$c" <---(2) │
│$echo $? ┃KORN>echo $? │
│0 ┃1 │
│$[""] ┃KORN>test $c = "cat" │
│$echo $? ┃KORN>echo $? │
│1 ┃0 │
└────────────────┸─────────────────┘

위의 예는 문자열 검색을 하는 방식을 보여주고 있다.
이 예에서는 이미 설명된 부분이지만 한번더 주목해야 할 부분이 있다.
(1)에서 보면은 변수에 저장되어 있는 문자열과 cat이라는 문자열을
비교하고 있다. 변수에 설정되어 있는 값은 cat이라는 문자와 함께
공백이 지정되어 있다. 공백도 분명한 하나의 문자이다. 그런데
비교 결과를 확인해보니 참인 값이 표시된다. 거짓이 되어야하는 데
왜 참일까? 그것은 이미 강의했던 쉘의 처리과정이 수행되며, 공백이
삭제되었기 때문이다.
(2)에서는 쌍따옴표를 사용하여 쉘이 공백을 제거하지 못하도록
한후 비교를 실행한 예이다. 결과가 거짓으로 표시되고 있다.


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



Shell Program의 조건 분기 방식 I

1. 조건 분기 제어문의 형식

대부분의 프로그래밍 언어와 마찬가지로 Bourne과 Korn Shell 프로그램에서도
if 제어문과 else 제어문을 사용하여 조건 분기를 수행할 수 있다.
또한 else 와 if 제어문을 결합한 elif 제어문도 함께 사용할 수 있다.

if 제어문

if 제어문은 프로그램내에서 특정의(지정한) 조건을 검사하여, 검사 결과에
따라서 프로그램의 흐름을 달리 처리할 수 있도록 해준다.

( 주의 ) if 제어문을 사용할 경우에, 반드시 if 제어문을 마감하는
fi 제어문을 지정해야 한다.

if 제어문의 형식은 다음과 같다.

if command_list
then
command
command
...
fi

위의 형식에서 command_list 부분에는 보통 test 명령문이 지정하여
검사 결과가 참이면 command들을 실행하고, 거짓이면 if 제어문을
종료한다. 또한 command_list 부분에 하나 또는 복수개의 명령어도 지정할
수도 있다. 만일 복수개의 명령을 지정했을 경우에는 마지막 명령어의
종료상태에 따라서 then과 fi 사이에 지정되어 있는 command들을 실행한다.
즉, command_list 상의 마지막 명령어의 종료상태가 0 이면 command들을
실행하고, 종료상태가 0 이 아니면 command들을 실행하지 않고 if 제어문을
종료하게 된다.

다음은 if 제어문의 기본형식을 보여주는 예제이다.

┌──────────────────────────┐
│ prog │
├──────────────────────────┤
│ # 이 프로그램은 │
│ # 화일이 존재하고 화일이 내용을 갖고 있는지를 │
│ # 검사하여, 그결과에 따라 분기를 수행한다. │
│ if test -s "$1" │
│ then │
│ echo $1 is an existing file │
│ fi │
└──────────────────────────┘

이 프로그램을 실행하면서 사용자가 명령어의 인자로서 제공한 화일에
대한 검사를 수행하여, 참일 경우에 echo 명령을 실행하고, 거짓일 경우에
프로그램을 종료한다.

2. if 제어문의 command_list상에 복수개의 명령어 지정

앞에서 설명한것 처럼 if 에서 then 문장사이에 하나 또는 그이상의
명령어들을 지정할 수 있다. if 제어문이 실행될때 이 명령어들은
모두 실행된다. 이때 지정된 명령어중 가장 마지막 명령어의 종료상태에
따라 then 과 fi 사이에 지정된 명령어들의 실행 여부가 결정된다.

다음은 이에 대한 예제이다.

┌────────────────────────────┐
│ proga │
├────────────────────────────┤
│ # 이 예제는 if 제어문에 여러개의 명령어들을 │
│ # command_list 부분에 지정하는 것을 보여주는 프로그램 │
│ # 입니다. │
│ if ls f1 │
│ who | grep user1 │
│ [ -f f1a ] │
│ then │
│ echo "The last command exited successful" │
│ echo "All other commands in command list\ │
│ may have failed" │
│ fi │
└────────────────────────────┘

$ls
f1
$proga <-------------- 위의 ls 명령에서 확인했듯이, 현재의
f1 디렉토리에는 f1a 화일이 없기때문에
user1 term/03 Aug 23 8:03 echo 명령이 실행되지 않았다. 지금의
$mv f1 f1a 출력은 command_list상에 지정된 명령의
$ls 실행 결과이다.
f1a
$proga <-------------- mv 명령을 통해 화일명을 f1a로 변경한후
user1 term/03 Aug 23 8:03 프로그램을 실행했다. 이 경우 조건이
The last command exited successful 참이되어 echo 명령이 실행된다.
All other commands in command list may have failed


3. else 제어문

if 명령문상에 지정된 검사결과가 0 이 아닌 경우에 즉 거짓일 경우에
실행할 명령어들을 따로 else 제어문을 이용하여 지정할 수 있다.

else 제어문을 사용할 경우의 if 제어문의 형식은 다음과 같다.

if command_list
then
command
command
....
else
command
command
....
fi

이전의 형식과 마찬가지로 command_list가 실행되고, 마지막 명령의
실행결과를 if 제어문이 검사하여, 결과가 참이면 then과 else 사이의
명령어들을 실행하고, 결과가 거짓이면 else 와 fi 사이의 명령어들을
실행한다.

다음은 else 제어문을 이용한 프로그램 예제이다.

┌───────────────────────────┐
│ prog │
├───────────────────────────┤
│ # 이 프로그램은 │
│ # 화일의 존재하고, 내용을 갖고 있는가를 검사하는 │
│ # 프로그램입니다. │
│ │
│ if [ -s "$1" ] │
│ then │
│ echo $1 has contents # 종료상태가 참인 경우 │
│ else │
│ echo $1 file error # 종료상태가 거짓인 경우 │
│ fi │
└───────────────────────────┘


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



Shell Program의 조건 분기 방식 II

4. 중첩된 if 제어문 ( Nested if statements )

우리는 프로그램을 작성할때 if 제어문안에서 또다른 조건분기를
필요로 하는 경우를 자주 경험한다. 쉘에서도 if 제어문을 이용한
이러한 프로그램 작성을 허용하는데, 이런 경우의 if 제어문을
중첩된 if 제어문이라 한다.

중첩된 if 제어문의 형식은 다음과 같다.

┌───── if command_list_A
│ then <------- command_list_A가 참인 경우에
│ command1 실행된다.
│ command2
│ ...
│ else <------- command_list_A가 거짓인
│ ┌─── if command_list_B 경우에 실행된다.
│ │ then <------- command_list_A가 거짓,
│ │ command3 command_list_B가 참인 경우
│ │ command4 실행된다.
│ │ ...
│ │ else <------------ command_list_A가 거짓,
│ │ ┌─── if command_list_C command_list_B가 거짓인
│ │ │ then <────┐ 경우에 실행된다.
│ │ │ command5 └─ command_list_A가 거짓,
│ │ │ command6 command_list_B가 거짓,
│ │ │ ... command_list_C가 참인
│ │ │ else <────┐ 경우에 실행된다.
│ │ │ command7 └─ Command_list_A,B,C
│ │ │ command8 모두가 거짓인 경우에
│ │ │ ... 실행된다.
│ │ └─── fi
│ └──── fi
└────── fi

이와같은 중첩된 if 제어문에서 주의할 것은 if 제어문의 갯수만큼
fi 문장이 기술되어야 한다는 것이다.

( 주의 ) if 제어문에 복수개의 command_list를 지정할수 있는데,
이 경우에도 마지막 명령의 종료상태에 따라 실행 흐름이 분기된다.

다음은 중첩된 if 제어문을 사용하고 있는 예제 프로그램들이다.

(예제 1 )

┌────────────────────────────────┐
│ Prog │
├────────────────────────────────┤
│ # 이 프로그램은 인자로서 화일명을 입력받아, 화일이 존재하는지를│
│ # 검사하고, 존재할 경우 화일에대한 읽기, 쓰기, 실행 권한을 │
│ # 검사하여 이에 상응하는 메세지를 표시하는 프로그램이다. │
│ │
│ file="$1" │
│ │
│ echo "Testing for file $file\n" │
│ │
│ if [ -f "$file" -o -d "$file" ] │
│ then │
│ echo "$file is a \c" │
│ if [ -r "$file" ] <----- 현재의 프로세스에게 │
│ then 읽기권한이 있는지를 검사 │
│ echo "readable \c" │
│ fi │
│ if [ -w "$file" ] <----- 현재의 프로세스에게 │
│ then 쓰기권한이 있는지를 검사 │
│ echo "writable \c" │
│ fi │
│ if [ -x "$file" ] <----- 현재의 프로세스에게 │
│ then 실행권한이 있는지를 검사 │
│ echo "executable \c" │
│ fi │
│ if [ -f "$file" ] <----- 화일이 일반화일인지를 검사 │
│ then │
│ echo "ordinary file.\n" │
│ else │
│ if [ -d "$file" ] <----- 화일이 디렉토리인지를 검사 │
│ then │
│ echo "directory.\n" │
│ fi │
│ fi │
│ else │
│ echo "$file is nonexistent.\n" <--- 화일이 존재하지 않을때 │
│ fi 표시되는 메세지 │
└────────────────────────────────┘

이 예에서 두가지를 주목해야 한다.

하나는 echo 명령에 지정된 \c와 \n문자의 의미이다.
\c는 다음 메세지를 연이어 기술할 것임을 뜻하며,
\n은 다음 라인에 다음 메세지를 기술할 것임을 의미한다.
또하나는 test 문에 사용된 변수들을 쌍따옴표로 둘러쌓았다는 것이다.
이유는 변수속에 있을지 모를 필드 구별자 또는 공백을
쉘로부터 제거되는 상황을 방지하기 위해서이다.

( 예제 2 )

┌────────────────────────────────┐
│prog │
├────────────────────────────────┤
│if [ $1 -gt 100 ] │
│then │
│ echo Number should be less than or equal to 100 │
│else │
│ if [ $1 -lt 0 ] │
│ then │
│ echo Number should be greater than or equal to 0 │
│ else │
│ if [ $1 -ge 90 ] │
│ then │
│ echo It is an A │
│ else │
│ if [ $1 -ge 80 ] │
│ then │
│ echo It is a B │
│ else │
│ if [ $1 -ge 70 ] │
│ then │
│ echo It is a C │
│ else │
│ if [ $1 -ge 60 ] │
│ then │
│ echo It is a D │
│ else │
│ echo It is an F │
│ fi │
│ fi │
│ fi │
│ fi │
│ fi │
│fi │
└────────────────────────────────┘

이 프로그램은 사용자가 인자로서 제공한1에서 100까지의 숫자를
구분하여 각각 상응하는 문자로 변환하는 프로그램이다. ( 성적처리를
연상하면 쉽게 이해가 갈 것이다.

이 프로그램에서는 직전의 예와 다르게 중첩된 if문을 지정하고 있다.
한번 비교해 보기 바란다.

5. elif 제어문

직전의 예를보면 중첩된 if 제어문을 사용하여 프로그램을 작성했을 경우
프로그램이 복잡해지는 것을 볼수 있다. 이럴 경우에 요긴하게 쓸수 있는
제어문으로, 즉; else와 if를 합한 형태의 elif 제어문을 이용하여
프로그램을 작성할 수 있다.

elif 제어문을 사용하는 if 제어문의 형식은 다음과 같다.

if commandA
then <--- commandA가 참일경우 수행
command1
command2
...
elif commandB <--- commandA가 거짓일 경우에 수행
then <--- commandA가 거짓이고,
command3 commandB가 참일 경우에수행
command4
...
else <--- commandA와 commandB가 모두 거짓
command5 일 경우에 수행
command6
...
fi

위의 형식에서 볼수 있듯이 elif 제어문을 사용하면 보다 용이하게
중첩된 if 제어문을 작성할수 있다.

다음은 elif 제어문의 예제이다.

┌────────────────────────────────┐
│prog │
├────────────────────────────────┤
│if [ $1 -gt 100 ] │
│then │
│ echo Number should be less than or equal to 100 │
│elif [ $1 -lt 0 ] │
│then │
│ echo Number should be greater than or equal to 0 │
│elif [ $1 -ge 90 ] │
│then │
│ echo It is an A │
│elif [ $1 -ge 80 ] │
│then │
│ echo It is a B │
│elif [ $1 -ge 70 ] │
│then │
│ echo It is a C │
│elif [ $1 -ge 60 ] │
│then │
│ echo It is a D │
│else │
│ echo It is an F │
│fi │
└────────────────────────────────┘

이 예는 이미 소개했던 프로그램이다. 그런데 이번에는 elif 제어문을
사용하여 프로그램을 보다 간결하게 작성한 결과를 보여준다.


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



Shell Program 실전 연습문제

1) 인자(Arguments)로써 하나의 화일명을 입력 받아서, 해당 화일이
실행 가능한지를 검사하는 쉘 프로그램을 작성하시오. 이때
검사 결과가 참일 경우에는 "화일명 is already executable"이라는
메세지를 표시한다. (이때 '화일명'은 사용자가 입력한 인자로 대치하여
출력한다. ) 만일 검사 결과가 거짓일 경우에는 해당 화일의 접근권한을
'rwxr-xr-x'로 변경을하고, "화일명 has been made executable"이라는
메세지를 표시하도록 한다. (이때의 '화일명'도 위와 동일하게
입력한 인자로 대치한다.)





2) 인자(Arguments)로써 화일명을 입력 받아서, 해당 화일의 화일 종류(file
type)를 확인하는 쉘 프로그램을 작성하시오. 이때 화일 종류가 "command
text file", "Ascii text file" 또는 "English text file"일 경우에는
pg 명령어를 사용하여 화일의 내용을 출력하게 하고, 만약 화일이
다른 화일 종류일 경우에는 "화일명 is not displayable."이라는 메세지를
표시한다. 그런데 화일이 기존에 존재하지 않는 화일일 경우에는 " File
화일명 does not exist"라는 메세지를 표시하도록 한다.





3) 다음과 같은 요구사항에 맞도록 해당 화일을 프린트 형식으로 변환한 후,
프린터로 출력하는 쉘 프로그램을 pr과 lp 명령어를 사용하여 작성하시요.

1. 프린트할 화일은 사용자에게 물어서 처리하도록 한다.
2. 프린트할 표제(Header)는 사용자에게 물어서 처리하도록 한다.
3. 프린트할 횟수를 사용자에게 물어서 처리하도록 한다.




4) 사용자에게 화일명과 디렉토리를 입력받은후, 사용자가 입력한 디렉토리부터
시작하여 하위 디렉토리들내에서 입력한 화일을 검색하여, 해당 화일을
발견할 경우 해당화일의 절대 경로명을 표시해 주는 쉘 프로그램을
작성하시요. 만일 해당 화일이 존재하지 않을 경우에는 다음과 같은
메세지를 표시한다. "File 화일명 is not under directory"

다음은 이런 쉘 프로그램을 실행한 예이다. 프로그램 작성시 참조하기
바란다.

$prog
ecter file name: f1
enter directory to search under /mjr
file f1 can be found as /mjr/f1 /mjr/dir/f1




5) 화일 복사를 위한 화일명과 디렉토리 명을 받아들이는 쉘 프로그램을
작성하시오. 화일의 복사는 입력받은 화일을 입력받은 디렉토리에 복사하는
형식으로 수행한다. 복사를 수행하기 전에 디렉토리에동일한 화일명이
존재하고 있는가를 확인하여, 있을 경우에는 사용자에게 overwrite, rename
또는 exit 를 묻는 메세지를 출력한후, 사용자의 선택에 따라서 작업이
실행되도록 처리한다. 즉; overwrite 선택시에는 그냥 복사를 수행하며,
rename을 선택할 경우에는 복사되는 화일명의 끝에 1을 붙여 복사되도록
처리하고, exit를 선택할 경우에는 프로그램 실행을 종료한다.




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



Shell Program: 실전 문제 해답

1.

# 이 프로그램은 사용자가 프로그램 실행시 지정한 화일이 사용자가
# 실행 가능한지 아닌지를 확인한 후, 실행 권한이 없을 경우,
# 실행 권한을 부여하는 프로그램이다.

if [ -x "$1" ]
then
echo "$1 is already executable"
else
chmod 755 $1
echo "$1 has been made executable"
fi

# 이 프로그램은 에러가 발생할 소지가 있다. 그것은 화일이 존재하지
# 않을 경우에 발생한다. 독자 여러분이 그 상황을 예방해 보기를
# 부탁한다.

2.
# 이 프로그램은 사용자가 입력한 화일이 화면에 표시 가능한지 또는
# 부가능한지를 확인한후, 표시 가능한 텍스트 화일인 경우 단말에
# 화일의 내용을 출력하는 프로그램이다. 이 프로그램은 시스템 Hang을
# 방지하기 위해 요긴하게 사용할 수 있는 프로그램이다.

if [ -x "$1" ]
then
file "$1" | grep 'text'
if [ $? = 0 ]
then
pg $1
else
echo "$1" is not displayable
fi
else
echo File $1 does not exist
fi

3.

# 이 프로그램은 사용자가 지정한 화일을 프린트하는 프로그램으로서
# 프린트하기 위해 사용자에게 몇가지 정보를 묻고 있다.

echo "Pls enter filename to print ?"
read printfile
echo "Pls enter page header to print ?"
read header
echo "Pls enter the number of copies to print ?"
read copies
pr -h "$header" "$printfile" | lp -n$copies

4.

# 이 프로그램은 특정 디렉토리내에서 지정한 화일의 존재여부를 확인하는
# 프로그램이다.

echo "Pls enter filename ?"
read sfile
echo "Pls enter directory to search under \c"
read sdirectory
vresults=`find $sdirectory -name $sfile -print`
if [ "$vresults" = "" ]
then
echo "File $sfile is not under $sdirectory"
else
echo "File $sfile can be found as $vresults"
fi

5.

# 이 프로그램은 사용자가 지정한 화일을 특정의 디렉토리에 복사하는데
# 있어서, 그 디렉토리내에 동일한 이름인 기존의 화일이 있을 경우에
# 처리 형태를 사용자에게 물어 처리하도록 하는 프로그램이다.

echo "Enter filename to copy: \c" ; read fname
echo "Enter destination directory name: \c" ; read dname

if [ ! ( -f $fname ) ]
then
echo "File $fname not found"
exit 1
fi
if [ ! ( -d $dname ) ]
then
echo "Directory $dname not found"
exit 1
fi
if [ -s ${dname}/${fname} ]
then
echo "File ${dname}/${fname} exists"
echo "Replace it (0=yes,other=no): \c" ; read choice
if [ choice -eq 0 ]
then
cp $fname ${dname}/${fname}
else
mv ${dname}/${fname} ${dname}/${fname}1
echo "move ${dname}/${fname} to ${dname}/${fname}1"
cp $fname ${dname}/${fname}
echo "File ${dname}/${fname} copied"
exit 0
fi
else
cp $fname ${dname}/${fname}
echo "File $fname copy to ${dname}/${fname}"
exit 0
fi

이들 프로그램들에서는 에러 발생시 처리 내용에 대한 제어를 정의하지 않고
있다. 수강자 여러분들께서 이 프로그램에 에러 처리 구문들을 추가하길
부탁한다.


--------------------------------------------------------------------------
위의 프로그램은 Bourne/Korn Shell 모두에서 실행되도록 작성한 프로그램입니다.
위의 프로그램이 모범 답안이라고 할수는 없으나, 여러분께서 쉘 프로그램을
이해하시는데 많은 도움을 얻으실 것이라고 믿습니다.
시간적 여유가 되신다면 보다 효율적인 프로그램이 되도록 수정하시기 바랍니다.
그리고 쉘 프로그래밍 과정에서는 각 주제제별 강의가 끝날때마다 계속적으로
실전문제를 드릴 예정이오니 많은 참고 바랍니다.
--------------------------------------------------------------------------


Filter와 Regular __EXPRESSION__s 개요

Filter와 Regular __EXPRESSION__s

이번 강의부터는 명령어 라인과 쉘 프로그램내에서 사용될수 있는
유닉스 필터(Filters)의 개요와 이용 방식을 소개한다. 그리고
필터내에서의 정규 표현식 (Regular __EXPRESSION__s)의 지정 방식에 대한
기본적인 소개가 행해진다.

1. 필터(Filter)

필터는 표준입력(Standard Input)을 취해서, 입력에 대해 제어를 수행하고,
그 결과를 표준출력(Standard Output)에 결과를 보내는/저장하는
프로그램이다. 이를 그림으로 설명하면 다음과 같다.

┏━━━━┓
┃ ┠─> 표준에러(Standard Error)
표준입력 ──> ┃ Filter ┃
(Standard Input) ┃ ┠─> 표준출력(Standard Output)
┗━━━━┛

필터는 두개의 프로그램사이의 파이프에서 사용되어질수 있는
프로그램으로서, 화일내의 정보를 조작하는데 유용하게 사용할 수 있다.
화일에서의 정보의 선택, 화일들간의 정보의 비교, 화일들간의 정보의
병합 또는 삭제와 같은 작업들을 필터를 사용하여 수행할 수 있다.

다음과 같은 몇가지 유닉스 쉘 명령어는 필터를 사용하여 작업을 수행한다.

- grep - sort - tr
- cut - paste - sed
- awk

2. 정규 표현식(Regular __EXPRESSION__s)

유닉스 필터내에서는 정규 표현식을 이용하여 입력 정보를 정리하여
처리할 수 있다. 정규 표현식은 작업시 일치(match)시킬 패턴(Pattern)을
나타내는 문자들의 그룹이다. 정규 표현식에는 일반 문자들 또는
특별한 대치가 수행되는 대치문자들을 사용하여 패턴을 지정할 수 있다.
이를 정리하면 다음과 같다.

1) 단순하게 패턴만을 지정하는 경우
- abc
2) 단순한 형태로결합하여 지정하는 경우
- ^a[0-9]
3) 복합 구조로 결합한 경우
- [aA][bB]c$
- ^\$50*\..*
- \.*

다음은 정규 표현식에서 사용되는 특수문자들을 나열한 것이다.

┌─────┬──────────────────────┬─────┐
│ Symbol │ 의 미 │ 예제 │
├─────┼──────────────────────┼─────┤
│[set] │지정된 set내의 문자중 한자와 대치된다. │[Aa][Bb] │
├─────┼──────────────────────┼─────┤
│[beg-end] │ASCII 코드를 기준으로 beg와 end 사이의 │[2-7] │
│ │한 문자와 대치된다. │ │
├─────┼──────────────────────┼─────┤
│. │임의의 한 문자와 대치된다. │b.t │
├─────┼──────────────────────┼─────┤
│* │선행하는 한 문자의 0 번 또는 그 이상의 │b.*t │
│ │occurrence와 대치된다. │b[A-z]*t │
├─────┼──────────────────────┼─────┤
│^ │라인의 처음에 지정되어 있는 문자 또는 │^root │
│ │문자열들만으로 제한. │ │
├─────┼──────────────────────┼─────┤
│[^set] │지정한 set에 속하지 않는 문자와 대치된다. │[^2-7] │
├─────┼──────────────────────┼─────┤
│$ │라인의 끝에 지정되어 있는 문자 또는 │/bin/ksh$ │
│ │문자열들만으로 제한. │ │
├─────┼──────────────────────┼─────┤
│\ │후속되는 한자의 특수문자에 대한 쉘의 처리를 │\$5\.00 │
│ │금지한다. │ │
└─────┴──────────────────────┴─────┘
( 이들에 대한 자세한 사용예및 설명은 후속되는 강의에서
하나하나씩 자세히 설명한다. )

정규 표현식에서 위와같은 특수문자들을 일반 문자로 사용하기 위해
쉘의 번역을 금지시키기 위해서는 이미 설명했던데로 보호문자들을 사용한다.
또한 정규 표현식에 공백 또는 탭 문자가 들어있을 경우에는 이들 문자는
필드 구별자로 쉘이 인식하여 처리하기 때문에, 사용시에 주의해야 한다.

정규 표현식(Regular __EXPRESSION__)은 사용자의 필요에 따라 하나 또는
그 이상의 특수문자들을 사용하여 지정할 수 있다. 그러나 지나치게 많은
특수문자를 사용하여 정규 표현식을 지정할 경우에는 주의해야 한다.
예를들자면 biginning^end와 같은 형식으로 사용된 ^문자는
쉘에 아무런 영향을 주지않고, 단순하게 ^ 문자로 처리된다.

앞절에서 설명했던 필터를 사용하는 유닉스 쉘 명령어들은, 이와같은
정규 표현식을 사용하여 보다 세부적인 작업 제어를 수행할 수있다.
다음 강의부터 몇회에 걸쳐 필터를 사용하는 쉘 명령어들의 응용 방식을
설명한다.


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


Filter,Regular Ex[ressions: grep

Regular __EXPRESSION__과 Filters: grep의 응용

grep 명령어도 내부적으로 filter를 사용하기 때문에 사용자는
정규 표현식을 사용하여 보다 다양한 형식으로 grep 명령어를
사용할 수있게 된다.

1. grep 명령어

grep(Global Regular __EXPRESSION__ Printer) 명령어는 사용자가 지정한
정규 표현식(Regular __EXPRESSION__)으로 지정된 문자열에 대해서
명령어 라인상에 지정된 화일/화일들의 내용을 검색하고,
지정한 문자열이 들어있는 화일상의 라인을 표시한다.

grep 명령어의 사용 형식은 다음과 같다.

grep [option] re [files]

여기에서 re는 정규 표현식을 나타낸다.
이때 정규 표현식을 명령어 라인상에 지정할 경우에는 정규 표현식내에
지정되어 있는 특수문자들에 대한 쉘의 잘못된 번역을 방지하기 위해서는
반드시 보호문자를 사용하여 묶어 주어야 한다.

그리고 만일 명령어 라인상에 하나 이상의 화일이 지정되면, grep 명령어는
검색 결과로 표시되는 각 라인의 처음에, 각 라인이 속한 화일명을 표시한다.

다음은 grep 명령어에 정규 표현식을 지정한 예를들기 위해 다음과 같은
화일이 있다고 가정한다.

┌─────────────────────────────┐
│/etc/passwd │
├─────────────────────────────┤
│root:x:0:1:root admin:/: │
│sa:x:0:0:sa menus login:/sa:/sa/sa.exec │
│user1:x:200:200:UNIX user:/usr/user1:/bin/rsh │
│user2:x:201:200:UNIX user:/usr/user1:/bin/rsh │
│user3:x:202:210:UNIX user:/usr/user3:/bin/rsh │
└─────────────────────────────┘

# 화일에서 user1이라는 문자열이 들어있는 라인을 검색한다.

$grep 'user1' /etc/passwd
user1:x:200:200:UNIX user:/usr/user1:/bin/rsh
user2:x:201:200:UNIX user:/usr/user1:/bin/rsh

# 화일에서 user1이라는 문자열이 처음에 지정되어 있는 라인을 검색한다.

$grep '^user1' /etc/passwd
user1:x:200:200:UNIX user:/usr/user1:/bin/rsh

# 화일내의 각 라인중 첫문자가 1-9까지의 숫자가 아닌 라인을 검색한다.

$grep '[^1-9]' /etc/passwd
root:x:0:1:root admin:/:
sa:x:0:0:sa menus login:/sa:/sa/sa.exec
user1:x:200:200:UNIX user:/usr/user1:/bin/rsh
user2:x:201:200:UNIX user:/usr/user1:/bin/rsh
user3:x:202:210:UNIX user:/usr/user3:/bin/rsh

# 화일내의 내용에서 13번째 문자부터 200이라는 문자열이 있는 라인을
# 찾아 표시한다.

$grep '^............200' /etc/passwd
user1:x:200:200:UNIX user:/usr/user1:/bin/rsh
user2:x:201:200:UNIX user:/usr/user1:/bin/rsh

# user3라는 문자열이 들어 있는 라인들을 검색한다.

$user=user3
$grep "$user" /etc/passwd
user3:x:202:210:UNIX user:/usr/user3:/bin/rsh

# 라인의 마지막 문자가 :인 라인들을 검색한다.

$grep ':$' /etc/passwd
root:x:0:1:root admin:/:

# 이 예의 정규 표현식은 심도있게 분석해야 한다.
# ++*라는 형식으로 지정되고 있는데 첫번째 + 지정은 문자 자체를 의미하나,
# 두번째 +는 후속되는 *라는 특수문자의 영향을 받기 때문에 +가 0번 또는
# 그 이상 반복되어 지정된 문자열을 의미한다. 따라서 이 예의 ++* 지정은
# +, ++, +++, ++++, +++++등이 검색된다.

$grep '++*' /etc/passwd
$

# 위의 예와 연관되는 예이다.
# 이번에 지정된 +* 정규 표현식은 + 문자가 없거나 있는, 즉 모든 라인을
# 검색하게 된다.

$grep '+*' /etc/passwd
root:x:0:1:root admin:/:
sa:x:0:0:sa menus login:/sa:/sa/sa.exec
user1:x:200:200:UNIX user:/usr/user1:/bin/rsh
user2:x:201:200:UNIX user:/usr/user1:/bin/rsh
user3:x:202:210:UNIX user:/usr/user3:/bin/rsh

2. grep 명령어의 옵션

grep 명령어는 명령어 실행후 표시되는 출력을 변형할수 있는 몇가지
출력을 제공한다.

다음은 grep 명령어가 제공하는 옵션들을 보여준다.

┌────┬─────────────────────────────┐
│ 옵션 │ 의 미 │
├────┼─────────────────────────────┤
│ -b │화일내 블럭번호를 함께 각각의 출력 라인의 앞에 표시한다. │
├────┼─────────────────────────────┤
│ -c │검색하여 발견한 라인수를 표시한다. │
├────┼─────────────────────────────┤
│ -i │검색시 대소문자를 구별하지 않는다. │
├────┼─────────────────────────────┤
│ -l │해당 문자열을 발견한 라인만을 표시한다. │
├────┼─────────────────────────────┤
│ -n │발견한 라인의 화일속의 라인 번호를 표시한다. │
├────┼─────────────────────────────┤
│ -s │에러 메세지를 표시하지 않는다. │
├────┼─────────────────────────────┤
│ -v │화일내의 라인중 지정한 문자열이 들어있지 않은 라인만을 │
│ │표시한다. │
└────┴─────────────────────────────┘

grep 명령어의 옵션을 설명하기 위해서 다음과 같은 화일이 있다고 가정한다.

┌─────────────────────────────┐
│/etc/passwd │
├─────────────────────────────┤
│root:x:0:1:root admin:/: │
│sa:x:0:0:sa menus login:/sa:/sa/sa.exec │
│user1:x:200:200:UNIX user:/usr/user1:/bin/rsh │
│user2:x:201:200:UNIX user:/usr/user2:/bin/rsh │
│user3:x:202:210:UNIX user:/usr/user3:/bin/rsh │
└─────────────────────────────┘

# -c 옵션을 사용하는 예

$grep -c 'user' /etc/passwd
3

# -i 옵션을 사용하는 예

$grep -i 'USER' /etc/passwd
user1:x:200:200:UNIX user:/usr/user1:/bin/rsh
user2:x:201:200:UNIX user:/usr/user2:/bin/rsh
user3:x:202:210:UNIX user:/usr/user3:/bin/rsh

# -v 옵션을 이용한 예

$grep -v 'user' /usr/passwd
root:x:0:1:root admin:/:
sa:x:0:0:sa menus login:/sa:/sa/sa.exec

# -l 옵션을 이용한 예

$grep -l '2[0-9][0-9]*' /etc/passwd
/etc/passwd

# -n 옵션을 이용한 예

$grep -n '[A-Z]' /etc/passwd
3:user1:x:200:200:UNIX user:/usr/user1:/bin/rsh
4:user2:x:201:200:UNIX user:/usr/user2:/bin/rsh
5:user3:x:202:210:UNIX user:/usr/user3:/bin/rsh

# 이 예에서 passwd라는 화일의 이름을 에러 처리 형식을 보여주기 위해
# 일부러 passswd라고 지정한 것을 주목하기 바란다.

$grep '.' /etc/passswd
grep:cannot open /etc/passswd

# 이 예에서는 -s 옵션을 사용하여 위의 예에서의 에러 처리형식을
# 제어하고 있다.

$grep -s '.' /etc/passswd /etc/passwd
root:x:0:1:root admin:/:
sa:x:0:0:sa menus login:/sa:/sa/sa.exec
user1:x:200:200:UNIX user:/usr/user1:/bin/rsh
user2:x:201:200:UNIX user:/usr/user2:/bin/rsh
user3:x:202:210:UNIX user:/usr/user3:/bin/rsh

# 이 예에서는 복수개의 옵션을 사용하는 예를 보여준다.

$grep -n -s '\.' /etc/passswd /etc/passwd
/etc/passwd:1:sa:x:0:0:sa menus login:/sa:/sa/sa.exec




'Academy I > Tech Academy' 카테고리의 다른 글

Unix 14 Shell Programming의 추가사항  (0) 2014.12.04
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 9 본쉘, 콘쉘  (0) 2014.12.04
Unix 8 쉘프로그래밍  (0) 2014.12.04
Unix 7 프로세스, 쉘  (0) 2014.12.04
Unix 6 유틸리티  (0) 2014.12.04