1. 위치 매개변수와 명령줄 매개변수
1) 위치 매개변수
-> 스크립트 이름 다음에 공백 문자로 구분된 각 단어를 매개변수라 부른다
-> 명령줄 매개변수는 스크립트 내에서 위치 매개변수로 참고할 수 있으며,
$1은 첫번째, $2는 두번째 매개변수를 의미한다
-> $9 이후 매개변수들은 중괄호를 이용하여 숫자부분을 ${10}처럼 묶어주어야 한다
-> 변수 $#은 전체 위치 매개변수의 개수를 알아보는데,
변수 $*는 전체 위치 매개변수의 내용을 알아보는데 사용한다
-> 위치 매개변수는 set 명령을 써서 설정(set) 또는 재설정(reset)할 수 있으며,
set명령을 사용하면 이전 설정값은 지워진다
-> 위치 매개변수
$0 : 현재 쉘 스크립트의 이름
$# : 위치 매개변수의 총 개수를 평가한다
$* : 모든 위치 매개변수를 평가한다
$@ : 큰 따옴표를 사용하였을 때를 제외하고는 $*와 동일한 의미
"$*" : "$1 $2 $3"으로 평가한다
"$@" : "$1" "$2" "$3"으로 평가한다
$1 ... ${10} : 사용가능한 위치 매개변수
$ cat test
#!/bin/bash
echo "This script is called $0."
echo "$0 $1 and $2"
echo "The number of positional parameters is $#"
$ ./test
This script is called ./test.
./test and
The number of positional parameters is 0
$ ./test aaa
This script is called ./test.
./test aaa and
The number of positional parameters is 1
$ ./test aaa bbb
This script is called ./test.
./test aaa and bbb
The number of positional parameters is 2
2) set 명령과 위치 매개변수
-> 위치 매개변수를 재설정하면 기존의 값들은 잃어버리게 된다
-> 모든 위치 매개변수의 설정을 해제하려면 'set --' 명령을 사용한다
-> $0는 언제나 스크립트 이름을 가리킨다
$ cat test
#!/bin/bash
echo The name of this script is $0
echo The arguments are $*
=> $*은 모든 위치 매개변수를 의미한다
set $(date)
echo The date is $2, $3, $6.
$ ./test a b c d
The name of this script is ./test
The arguments are a b c d
The date is 11., 21., KST.
$ cat test
#!/bin/bash
name=${1:?"requires an argument"}
echo Hello $name
=> 특수 변수 변경자인 :?는 $1이 값을 가지고 있는지 검사한다
=> 지정한 값이 없으면 스크립트를 종료하고 메시지를 출력한다
$ ./test
./test: line 2: 1: requires an argument
$ ./test aaa
Hello aaa
-> $*와 $@의 차이
=> $*와 $@의 의미는 큰 따옴표 안에서 사용되는 경우에만 서로 다르다
=> 큰 따옴표 안에 $*를 사용하는 경우에는 매개변수들이 단일 문자로 취급된다
=> $@를 큰 따옴표 안에 사용하면 매개변수들은 서로 분리된 문자로 인식된다
$ set 'aaa bbb' ccc ddd
$ for i in $*
> do
> echo $i
> done
aaa
bbb
ccc
ddd
$ set 'aaa bbb' ccc ddd
$ for i in "$*"
> do
> echo $i
> done
aaa bbb ccc ddd
> $*이 큰 따옴표 안에 들어있기 때문에 전체 매개변수를 하나의 문자열로 처리한다
$ set 'aaa bbb' ccc ddd
$ for i in $@
> do
> echo $i
> done
aaa
bbb
ccc
ddd
$ set 'aaa bbb' ccc ddd
$ for i in "$@"
> do
> echo $i
> done
aaa bbb
ccc
ddd
> $@가 큰 따옴표 안에 있으므로 각 위치 매개변수를 개개의 문자열로 처리한다
2. 조건의 표현과 흐름 제어
1) 종료 상태
-> 종료 상태는 명령이 성공했는지 실패했는지 검사하는 것이고 다른 하나는 표현식이 참인지 거짓인지
판별하는 것이다
-> 종료 상태가 0이면 성공이나 참을 의미한다
-> 종료 상태가 0이 아니면 실패나 거짓을 의미한다
-> ? 변수는 가장 마지막으로 실행된 명령의 종료 상태를 보관한다
2) test 내장 명령
-> 단일 대괄호와 test 명령
=> 표현식의 평가에는 일반적으로 test 명령을 사용한다
=> test 명령은 대괄호 ([ ])로도 표현할 수 있다
=> test 명령이나 대괄호를 사용하여 식을 평가할 때, 쉘은 메타문자를 전개하지 않는다
=> 변수에 값을 저장할 때는 단어 단위로 나뉘어 저장하게 되므로 스페이스를 포함한 문자열은
따옴표로 묶어준다
-> 이중 대괄호와 test 명령
=> 배시 2.x 버전 이상에서는 이중 대괄호 ([[ ]])를 사용하여 표현식을 평가할 수 있다
=> 단어를 분리하여 변수에 저장하지 않고 패턴 검색이 가능할 뿐만 아니라,
메타문자에 대한 해석도 수행한다
=> 스페이스를 포함하는 문자열에는 반드시 따옴표를 사용하여야하며,
스페이스의 포함 여부와 상관없이 정확히 일치하는지의 여부를 검사할 때도 따옴표를
사용하여야 한다
=> test 명령에서는 논리연산자 &&(and), ||(or)를 -a, -o 옵션으로 치환하여 사용할 수 있다
-> test 명령 연산자
=> 문자열 검사
[ string1 = string2 ] : string1은 string2와 같다
[ string1 == string2 ] : string1은 string2와 같다
[ string1 != string2 ] : string1은 string2와 다르다
(!= 양족에는 스페이스를 사용하여야 한다 )
[ string ] : string은 널이 아니다
[ -z string ] : string의 길이가 0 이다
[ -n string ] : string의 길이가 0이 아니다
[ -l string ] : string의 길이(문자의 개수)
=> 논리 검사
[ string1 -a string2 ] : and 연산자
[ string1 -o string2 ] : or 연산자
[ ! string1 ] : not 연산자
=> 논리검사(복합 test 명령)
[[ pattern1 && pattern2 ]] : pattern1과 pattern2가 모두 참
[[ pattern1 || pattern2 ]] : pattern1과 pattern2 중에 하나가 참
[[ ! pattern ]] : pattern과 같지 않다
=> 정수 검사
[ int1 -eq int2 ] : int1은 int2와 같다
[ int1 -ne int2 ] : int1은 int2와 다르다
[ int1 -gt int2 ] : int1은 int2보다 크다
[ int1 -ge int2 ] : int1은 int2보다 크거나 같다
[ int1 -lt int2 ] : int1은 int2보다 작다
[ int1 -le int2 ] : int1은 int2보다 작거나 같다
=> 파일 검사를 위한 이항 연산자
[ file1 -nt file2 ] : file1이 file2보다 새로운 파일이면 참
[ file1 -ot file2 ] : file1이 file2보다 오래된 파일이면 참
[ file1 -ef file2 ] : file1과 file2가 동일한 장치이거나 같은
inode 번호를 가면 참
$ name=kyoung
$ grep "$name" /etc/passwd
kyoung:x:1000:1000:kyoung,,,:/home/kyoung:/bin/bash
$ echo $?
0
$ test $name != kyoung
$ echo $?
1
$ [ $name = kyoung ]
$ echo $?
0
$ [ $name = [Kk]????? ]
$ echo $?
1
> test 명령에서는 와일드 카드를 사용할 수 없다 따라서 ?를 일반 문자로 취급한다
-> let 명령과 이중 괄호로 산술연산 처리
=> 산술식 평가에도 test 명령을 사용할 수 있지만, C언어에서처럼 풍부한 연산자를 사용하고 싶다면
let 명령의 사용을 권장한다
=> let 명령은 이중 소괄호 ((( )))로 치환하여 사용할 수 있다
=> let 명령 연산자
- : 음수
+ : 양수
! : 논리 부정
~ : 비트 단위 부정
* : 곱셈
/ : 나누기
% : 나머지
+ : 덧셈
- : 뺄셈
bash 2.x 이후 추가된 let 연산자
<< : 비트 단위 왼쪽 시프트
>> : 비트 단위 오른쪽 시프트
<= >= <> : 비교 연산자
== != : 등호 연산자
& : 비트 단위 and
^ : 비트 단위 배타적 or
| : 비트 단위 or
&& : 논리곱(and)
|| : 논리합(or)
= *= /= %= += -=
<<= >>= &= ^= |= : 대입 및 복합 연산자
$ x=2
$ y=3
$ (( x > 2 ))
$ echo $?
1
$ (( x < 3 ))
$ echo $?
0
$ (( x == 2 && y == 3 ))
$ echo $?
0
$ (( x > 2 || y < 3 ))
$ echo $?
1
- if 명령
-> if문은 종료 상태 값이 0이면 명령이 성공적으로 수행되었음을
의미하고 키워드 then 다음의 문장들이 실행된다
-> 형식
if 명령
then
명령
명령
fi
=> 숫자 및 문자열에 대해 test 사용 - 구형식
if test 조건식
then
명령
fi
또는
if [ 문자열/수식 ] then
명령
fi
=> 문자열에 대해 test 사용 - 새형식
if [[ 문자열 조건식 ]] then
명령
fi
=> 숫자에 대해 let 사용 - 새형식
if (( 수식 ))
$ shopt -s extglob
> shopt 내장 명령이 extglob을 설정하면 확장 메타 문자를 쓸 수 있다
$ answer="not really"
$ if [[ $answer = [Nn]o? ( way|t really ) ]]
then
echo "So sorry."
fi
> 확장 패턴 검색을 사용한다
> 이 표현식에 따르면 answer 변수의 값이 no나 No로 시작하고 괄호 안에
있는 표현식이 0번 또는 1번 나오면 전체 표현식은 참이 된다
-> exit 명령과 변수
=> exit 명령은 스크립트를 중지시키고 명령줄로 돌아가고자 할 때 사용한다
$ if (( $# != 2 ))
then
echo "Usage: $0 mdays size " 1>&2
exit 1
fi
> 매개변수가 2개가 아니면 오류 메시지를 표준 오류로 출력시키고 스크립트의
종료 상태를 1로 반환하고 종료한다
-> 널 값 검사
=> 변수가 널인지 검사할 때는 널의 표현에 큰 따옴표를 사용하여야한다
그렇지 않으면 test 명령이 실패한다
$ if [ "$name" = "" ]
then
echo The name variable is null
fi
> 변수 name의 값이 널이면 검사 결과는 참이되며 큰 따옴표는 널을 표현하기 위해
사용한다
3) if/else 명령
-> if/else 명령은 양자 택일을 표현하는데 사용한다
-> 형식
if 명령
then
명령(들)
else
명령(들)
fi
$ if grep "$name" /etc/passwd >& /dev/null; then
echo Found $name!
else
echo "Can't find $name."
exit 1
fi
4) if/elif/else 명령
-> 다중 택일 표현이다
-> 형식
if 명령
then
명령(들)
elif 명령
then
명령(들)
elif 명령
then
명령(들)
else
명령(들)
fi
5) 파일 검사
-> 스크립트를 작성하다 보면 특정 파일에 대한 정보가 필요한 경우 사용
-> 파일의 권한, 형식, 기본속성 등을 확인 할 때 사용한다
-> 파일 검사 연산자
-b filename : 블럭 파일
-c filename : 문자 파일
-d filename : 디렉토리가 존재
-e filename : 파일이 존재
-f filename : 파일이 존재하고, 디렉토리가 아님
-G filename : 파일이 존재하고, 유효 그룹 ID의 소유이다
-g filename : setGID가 설정되어 있다
-k filename : 스티키 비트가 설정되어 있다
-L filename : 파일이 심볼릭 링크이다
-p filename : 파일이 명명된 파이프 이다
-O filename : 파일이 존재하고, 유효 사용자 ID의 소유이다
-r filename : 파일이 읽기 가능하다
-S filename : 파일이 소켓이다
-s filename : 파일 크기가 0이 아니다
-t fd : 파일 식별자가 터미널에 열려있다
-u filename : setUID가 설정되어 있다
-w filename : 파일에 쓰기가 가능하다
-x filename : 파일이 실행 가능하다
if [ -d $file ]
then
echo "$file is a directory"
> file이 디렉토리이면 출력
elif [ -f $file ]
> file이 디렉토리가 아니면 다시 검사
then
if [ -r $file -a -w $file -a -x $file ]
then
echo "You have read, write, and execute \ permission on $file."
fi
> file이 읽기,쓰기,실행 모두 가능하면 출력
else
echo "$file is neither a file nor a directory."
> 위 내용이 모두 거짓이면 출력
fi
6) null 명령
-> null 명령은 내장 명령으로서 콜론(:)으로 나타낸다
-> 실제 아무런 작업도 하지 않으며, 단지 종료 상태를 0으로 돌려준다
-> 주로 if 다음에 어떤 작업도 지정하고 싶지 않을 때 사용한다
-> then 다음에 문장을 기술하지 않으면 오류가 발생하기 때문에, 무한 반복문을 작성할 때도
사용한다
name=Tom
if grep "$name" databasefile >& /dev/null
then
:
else
echo "$1 not found in databasefile"
exit 1
fi
> 콜론(:)은 널 명령으로 종료 상태 값을 0으로 되돌리기 외에 아무 일도
하지 않는다
$ DATAFILE=
$ : ${DATAFILE:=$HOME/db/datafile}
$ echo $DATAFILE
/home/jody/ellie/db/datafile
> 콜론(:) 명령은 아무것도 하지 않는 명령이다
> 변경자 :=에 의해 널인 변수 값이 지정한 값으로 치환된다
> 변경된 값은 DATAFILE에 영구적으로 설정된다
7) case 명령
-> 다중 분기 명령으로 if/elif 대신 사용한다
-> case문의 변수가 value1, value2, ... 중의 하나와 일치하면 그 값 이후부터 이중 세미콜론(;;)이
나올때까지의 명령들이 실행된다
-> 수행 후에는 esac 다음으로 프로그램의 제어가 옮겨 진다
-> case 변수와 대응되는 것이 없으면, *) 기호 다음에서 부터 esac 사이의 문장들을 수행한다
-> *)의 역할은 if/else 조건문에서 else 문장과 동일하다
-> case 변수의 값에는 와일드 카드나 |(OR연산자)를 사용할 수 있다
-> 형식
case 변수 in
value1)
명령(들)
;;
value2)
명령(들)
;;
*)
명령(들)
;;
esac grep 단어 파일명 파일명 ...
$ cat test
echo -n "Choose a foreground color for your xterm window: "
read color
case "$color" in
[Bb]l??)
xterm -fg blue -fn terminal &
;;
[Gg]ree*)
xterm -fg darkgreen -fn terminal &
;;
red | orange)
xterm -fg $color -fn terminal &
;;
*)
xterm -fn terminal
;;
esac
echo "Out of case command"
-> here 문서와 case 명령을 이용한 메뉴 구성
=> here 문서와 case 명령은 함께 사용되는 경우가 잦다
=> here 문서는 화면에 출력될 메뉴를 구성하는 데 사용하며, case 명령은
사용자의 선택을 검사하여 적절한 메뉴 항목을 수행시킨다
$ cat test
echo "Select a terminal type: "
cat <<- ENDIT
1) unix
2) xterm
3) sun
ENDIT
read choice
case "$choice" in
1) TERM=unix
export TERM
;;
2) TERM=xterm
export TERM
;;
3) TERM=sun
export TERM
;;
esac
echo "TERM is $TERM."