셸 프로그래밍 이란 셸에서 사용되는 여러 명령어들을 모아 하나의 파일로 만드는 과정이다. 이를 통해 만들어진 프로그램을 셸 스크립트 라고 한다. 개발 언어가 가지고 있는 반복문, 조건문, 변수, 함수 등의 특성을 모두 가지고 있어 다양한 명령어와의 조합을 통해 사용자의 환경에 맞게 프로그래밍 할 수 있다.
// 편집기를 사용해 파일을 생성한다.
$ vi who2.sh
// 첫 번째 줄에 사용할 셸을 명시한다
#!/bin/bash
// 두 번째 줄부터 원하는 구문을 입력한다.
echo "Login List"
who
// 편집기를 빠져나와 스크립트에 실행 권한을 부여한다.
$ chmod 775 who2.sh
// 실행시킨다
$./who2.sh
사용자의 홈디렉터리에서 스크립트를 실행하려면 파일명 맢에 반드시 ./ 를 붙여야 한다. 이 과정없이 계속적으로 사용하려면 PATH에 홈 디렉터리를 등록하거나 PATH에 등록된 경로로 파일을 옮겨야 한다.
# 실행 권한 을 부여하지 않고 실행하려면 다음과 같은 방법을 사용할 수 있다.
# 앞부분에 sh 명령어 붙이기
$ sh. show2.sh
# 앞부분에 source 명령 붙이기
$ source who2.sh
# 앞부분에 . 붙이기
$ .who.2sh
변수 는 어떠한 데이터를 저장하는 공간이다. 셸에서 변수는 문자형만을 가지며 별도의 변수형 선언이 필요없다.
var=value 와 같은 형태로 변수에 값을 저장한다. 변수를 사용할 때는 변수명 앞에 $를 붙인다. 값을 할당할 때 대입연산자 =를 사용하고 연산자와 피연산자 사이에 공백이 존재해서는 안된다. 변수명은 - 를 제외한 특수문자로 시작해서는 안된다.
$ vi test.sh
#!/bin/bah
num=2
## num 변수에 할당된 값으로 치환해 출력한다.
echo "this is the ${num}nd"
$ vi var.sh
#!/bin/bash
## := var 변수가 null 이면 ls의 결과를 할당하고, var 이 null 이 아니면 그 값을 그대로 사용한다.
var=
${var:=ls}
echo $var
$ echo $DIR
## -= 변수 DIR에 할당된 값이 있다면 그 값을 사용하고 없으면 temp를 사용한다. 단, 할당하지는 않는다.
$ echo ${DIR:-temp}
$ vi test4.sh
## 아규먼트 변수
#!/bin/bash
## $0 : 실행된 셸 스크립트명
echo "This script file $0"
## $0 : 스크림트에 넘겨진 아규먼트의 개수
echo "Argument Count is $#"
## $0 : 셸 스크립트의 PID
echo "Process ID is $$"
## $* : 스크립트에 전달된 인자 전체를 하나의 변수에 저장하면서 IFS 변수의 첫 번째 문자로 구분
echo "Argument List \$* : $*"
## $@ : $*와 동일
echo "Argument List \$@ : $@"
## $1 : 스크립트에 넘겨진 첫 번째 아규먼트
echo "Argument 1 : $1"
## $2 : 스크립트에 넘겨진 두 번째 아규먼트
echo "Argument 2 : $2"
## $3 : 스크립트에 넘겨진 세 번째 아규먼트
echo "Argument 3 : $3"
$ ./test4.sh a b c
This script file ./test4.sh
Argument Count is 3
Process ID is 31779
Argument List $* : a b c
Argument List $@ : a b c
Argument 1 : a
Argument 2 : b
Argument 3 : c
Shell 변수 관련 명령어 로는 set, env, export, unset 이 있다.
- set : 일반적으로는 셸 변수를 추력하는 명령이다. 셸의 환경과 관련된 변수, 함수 등도 출력한다.
- env : 환경 변수를 출력하는 명령이다. export 된 변수는 이 명령으로만 확인해야하고 사용자가 선언한 셸 변수 정보도 함께 출력된다.
- export : 특정 변수의 범위를 환경 데이터 공간으로 전송하여 자식 프로세스에서도 사용 가능한게 한다. 전역 변수의 개념이다.
- unset : 선언된 변수를 제거한다.
$ user1=lin
# 셸 변수로 선언한 user1인 경우 set 명령어로 확인할 수 있다.
$ set | grep ^user.
user1=lin
# set으로 선언한 셸 변수는 env 명령어로 확인할 수 없다.
$ env | grep ^user.
$ export user2=joon.
# export로 선언한 user2는 set, env 명령어 모두로 확인할 수 있다.
$ set | grep ^user.
user1=lin
user2=joon
$ env | grep ^user.
user2=joon
$ vi main.sh
echo shell programming
name=test
city=seoul
echo name is $name
echo city is $city
export name
./sub
echo stop main
$ vi sub
echo start sub
echo name is $name
echo city is $city
echo stop sub
$ chmod 775 main sub
$ ./main
shell programming
name is test
city is seoul
start sub
name is test
## city 변수는 Export 되지 않았기 때문에 출력되지 않는다.
city is
stop sub
stop main
# 셸 변수로 선언된 user를 제거할 때는 unset 명령을 사용한다.
$ user=posein
$ echo $user
posein
$ unset user
$ echo $user
echo문 은 라인을 제어하는 명령해석기이며 -e 옵션과 같이 \로 시작하는 escape 특수 문자를 사용할 수 있다.
$ echo "Hi\nHello"
Hi\nHello
# New Line(\n) : 줄바꿈한다.
$ echo -e "Hi\nHello"
Hi
Hello
# 폼피드(\f) : 앞문자열의 만큼 열을 밀어 이동시킨다.
$ echo -e "Hi\fHello"
Hi
Hello
# 케리지리턴(\r) : 앞 문자열의 앞부분부터 뒷 문자열 만큼 대체한다.
$ echo -e "Five word\rHello"
Helloword
# tap(\t) : 탭만큼 띄운다.
$ echo -e "H\tHello"
Hi Hello
조건식은 명령행에서는 test 명령어를 이용해 간단히 비교할 수 있고, 셸 스크립트에서는 if 문과 같이 사용된다. 셸 스크립트에서 사용할 경우에 test 라는 명령어를 생략하고 [] 를 이용한다. 테스트 조건은 '[' 와 ']' 사이에 입력하고, '[' 와 ']' 사이에는 반드시 공백 문자가 들어가야 한다.
test [표현식]
# [string] : string이 빈 문자열이 아니라면 참(0)
# [string = string2] : 두 문자열이 같다면 참(0)
# [string != string2] : 두 문자열이 다르면 참(0)
# [-n string] : 문자열이 null 이 아니라면 참
# [-z string] : 문자열이 null 이면 참
# [-z string] : 문자열이 null 이면 참
# [exp1 -eq exp2] : 두 표현식 값이 같다면 참
# [exp1 -ne exp2] : 두 표현식 값이 같지 않다면 참
# [exp1 -gt exp2] : Greater then
# [exp1 -ge exp2] : Greater Equal
# [exp1 -lt exp2] : Less Then
# [exp1 -le exp2] : Less Equa;
# [!expr] : expr이 참이면 거짓, 거짓이면 참
# [expr1 -a expr2] : AND 연산의 결과
# [expr1 -o expr2] : OR 연산의 결과
# [-b FILE] : FILE 이 블록 디바이스이면 참
# [-c FILE] : FILE 이 문자 디바이스이면 참
# [-d FILE] : FILE 이 디렉터리이면 참
# [-e FILE] : FILE 이 존재하면 참
# [-f FILE] : FILE 이 존재하고 정규 파일이면 참
# [-g FILE] : FILE 이 SGID가 있으면 참
# [-k FILE] : FILE 이 Sticky bit가 있으면 참
# [-L FILE] : FILE 이 실볼릭 링크이면 참
# [-p FILE] : FILE 이 Named pipe 이면 참
# [-r FILE] : FILE 이 현재 사용자가 읽을 수 있는 파일이면 참
# [-s FILE] : FILE 이 비어있지 않으면 참
# [-S FILE] : FILE 이 소켓 디바이스 이면 참
# [-t FILE] : File Descriptor 가 열려진 터미널이면 참
# [-u FILE] : FILE 이 SUID가 있으면 참
# [-w FILE] : 현재 사용자가 쓸 수 있는 파일이면 참
# [-x FILE] : 현재 사용자가 실행할 수 있는 파일이면 참
# [-O FILE] : FILE 의 소유자가 현재 사용자이면 참
# [-G FILE] : FILE 의 그룹이 현재 사용자의 그룹과 같으면 참
# [FILE1 -nt FILE2] : FILE1 이 FILE2 보다 최신 파일이면 참
# [FILE1 -ot FILE2] : FILE1 이 FILE2 보다 오래된 파일이면 참
# [FILE1 -et FILE2] : FILE1 이 FILE2의 하드링크 이면 참
# 명령행에서 간단히 테스트가 가능하다.
$ test 20 -gt 30
$ echo $?
1
$ test 20 -lt 30
$ echo $?
0
# 로그인한 사용자가 yuloje가 아니므로 거짓(1) 을 반환한다.
$ vi ex.sh
["$LOGNAME" = "yuloje"]
echo $?
$ source ex.sh
1
조건문(if) 은 참/거짓을 판별하기위해 사용된다. if로 시작하면 반드시 fi 로 끝내야 한다.
# if [ 조건1 ]
# then
# 실행문장2
# then
# 실행문장2
# else
# 실행문장3
# fi
$ vi test.sh
if [ "$SHELL" = "/bin/bash" ]
then
echo "your login shell is the bash (bourne again shell)"
else
echo "your login shell is not bash but $SHELL"
fi
case 문 은 문자열과 일치치하는 정규식 부분을 찾아 해당하는 정규식 다음에 있는 명령어를 실행시킨다.
# case 문자열
# in
# 정규식1) 명령어;;
# 정규식2) 명령어;;
# ...
# esac
$ vi cal
#!/bin/bash
echo "*************"
echo "* 보 기 "
echo "*************"
echo "수행하고자하는 명령어는?(번호입력)"
read number;
case $number in
1) who;;
2) date;;
3) pwd;;
4) ls -l;;
*) echo "없는 번호입니다";;
반복문 을 통해 지정된 숫자나 몇 가지 조건을 만날 때까지 일련의 명령을 반복적으로 수행할 수 있다.
# for 문
# for 변수 in 값1, 값2
# do
# 실행문장
# done
$ vi tt4
for flower in Rost Tulip Lily
do
echo $flower
done
$ ./tt4
# while 문
# while 조건문
# do
# 실행문장
# done
$ vi tt7
var = 1
while ["$var" -le 5]
do
echo $var
var=`expr $var + 1`
done
# until 문
# 조건이 거짓일 동안 루프를 수행한다
# until 조건문
# do
# 실행문장
# done
$ vi tt9
COUNTER=20
until [$COUNTER -lt 10]
do
echo COUNTER is $COUNTER
COUNTER=`expr $COUNTER -1 `
done
함수 란 일종의 '스크립트 안의 스크립트' 이다. 명령어 그룹을 메모리 내에 정의하여 수행 속도를 향상시킨다.
# functionName()
# {
# comaand
# }
# function functionName
# {
# command
# }
$ vi fun
#!/bin/bash
user_print()
{
echo "shell programming"
}
echo "start user_print"
user_print
echo "end user_print"
$ ./fun
패턴과 패턴비교 는 일종의 문자열을 연산하는 것으로 특정한 패턴을 놓고 변수의 문자열 값이 일부분이라도 이 패턴과 일치하는지 검사할 때 쓰인다.
# ${variable#pattern} 처음부터 pattern 과 맞는 variable의 부분을 찾아
# 이 중 가장 작은 부분을 제거하고 나머지를 반환한다.
# ${variable###pattern} 처음부터 pattern 과 맞는 variable의 부분을 찾아
# 이 중 가장 큰 부분을 제거하고 나머지를 반환한다.
# ${variable%pattern} 끝에서부터 pattern과 일치하는
# variable의 최소 부분을 제거하고 나머지를 반환한다.
# ${variable#pattern} 끝에서부터 pattern과 일치하는
# variable의 최대 부분을 제거하고 나머지 반환
$ vi ex3
#!/bin/bash
unset var1
echo ${var1:-string2}
var1=string1
echo ${var1:-string2}
var1=/etc/sysconfig/network
echo ${var1#*/}
echo ${var1##*/}
var2=/etc/sysconfig/network-scripts/sysconfig/ifcfg-lo
echo ${var2%sysconfig*}
echo ${var2%%sysconfig*}
exit 0
리눅스에서 사용되는 종료 코드 는 0~255까지 이다. 0은 성공을 뜻하고 1 이상은 오류를 나타낸다.
- 0 : 성공
- 1 : 일반적인 오류
- 2 : 셀 내장 명령의 틀린 사용
- 126 : 파일이 실행 가능하지 않음
- 127 : 명령어를 찾을 수 없음
- 128 : 종료할 때 잘못된 인수 적용
- 128+n : 치명적인 시그널 에러
- 130 : [Ctrl] + [c] 키 조합에 의한 종료
우선순위 는 alias, 지정 키워드, 함수, 내장 명령어, 스크립트, 프로그램, 환경변수 내부의 디렉터리 실행 순이다.
셸 프로그래밍 관련 명령어에는 다음과 같은 것들이 있다.
read 는 임의의 값을 키보드로부터 입력 받는 명령어로 연속으로 여러 값을 받을 수 있다. 인자값은 차례로 처리된다.
read 변수1 [변수2, ...]
$ read flower
rose
$ echo $flower
rose
$ read var1 var2
lin jonn
$ echo $var1 $var2
lin joon
$ vi read1
#!/bin/bash
echo "두 자연수 빼기"
echo "첫 번째 자연수는?"
read int1
echo "두 번째 자연수는?"
read int2
if [ $int1 -ge $int2]
then
expr $int1 - $int2
else
expr $int2 - $int1
fi
$ source ./read1
첫 번째 자연수는?
6
두 번째 자연수는?
4
2
echo 명령어는 해당 문자열의 표준 출력에 표시하고 행 바꿈을 한다. 만약 문자열이 없으면 행 바꿈만 실행한다.
echo [option][문자열]
option
- n : 출력 결과에서 행 바꿈을 하지 않는다
- e : escape 문자를 사용할 수 있게 해준다.
$ echo Hi!
Hi!
// 행 바꿈을 사용해서 출력한다.
$ echo -e "Yes\nNo"
Yes
No
break 는 for, while, until 등의 순환문에서 빠져 나온다.
break [n] n번째 루프에서 빠져나온다.
$ vi br1
#!/bin/bash
rm -rf file*
# file1 라는 파일을 생성
echo > file1
# file2 라는 파일을 생성
echo > file2
# file3 라는 디렉터리
mkdir file3
# file4 라는 파일을 생성
echo > file4
# file로 시작하는 개체를 돌면서
for var in file*
do
# 해당 개체가 디렉터리이면
if [ -d "$var"]
then
# 순환문 탈출
break;
fi
done
# 탈출시점에 var 변수에 할당된 개채 출력
echo "directory name : $var"
## 로그아웃
exit 0
countinue 명령어는 for, while, until 등의 순환문을 계속해서 실행한다.
coutinue [n]
$ vi co1
#!/bin/bash
rm -rf file*
echo > file1
echo > file2
mkdir file3
echo > file4
for var in file*
do
if [-d "$var"]
then
continue
fi
echo "file name : $var"
done
exit 0
$ ./co1
file name : file1
file name : file2
file name : file4
exit 명령어는 상태 코드 n과 함께 스크립트를 종료한다. n은 성공이나 0이 아닌 숫자가 될 수 있다. 만약 n이 주어지지 않으면 가장 최근에 사용된 명령의 상태 코드를 사용한다.
exit [n]
function 명령어는 함수를 정의한다. 위치 매개 변수가 명령어 부분에서 사용 가능하다.
function 함수명 { 명령어 }
$ vi myfunc
#!/bin/bash
function myfunc
{
echo "parameter is $1"
}
$ ./myfunc love
parameter is love
getopts 명령어는 명령행 인자를 처리해 올바른지 점검한다. 주로 셸 스크립트 순환문에서 사용되며 명령행 옵션이 표준 형식에 맞는지 확인하다.
return 명령어는 함수정의에서 사용되며 함수가 종료될 때 상태 코드 n을 반환한다. 만약 n이 생략되면 직전에 실행된 명령 상태 코드를 반환하다.
return [n]
set 명령어는 특별한 옵션 없이 set 이라고 입력하면 셸 변수와 값을 출력한다. set 은 셸에서 보통 변수와 값을 선언할 때 주로 사용하는데 bash 에서는 생략해서 사용한다.
set [option] [문자열]
option
-f : 셸에서 *,?와 같은 와일드카드 문자를 이용한 확장을 없앤다.
-C : 리다이렉션 기호인 > 를 이용한 파일의 덮어쓰기를 제한한다.
-o : bash의 환경 설정 관련 값을 확인하고 설정하는 옵션이다.
# 셀 변수와 값을 출력한다.
$ set
# a라는 변수에 값 1을 지정한다. 그냥 a=1 형식만으로도 선언가능하다.
$ set a=1
$ set a b c
$ set 명령으로 설정된 매개인자를 확인할 수 있다.
$ echo $1 $2
a b
# 설정된 bash 환경 설정 값들을 확인한다.
$ set -o
# *,? 와 같은 와일드카드 문자의 기능을 없앤다.
$ set -o noglob
# *,?와 같은 와일드카드 문자의 기능을 활성화한다.
$ set +o noglob
# 리다이렉션 기호인 >를 이용한 파일의 덮어쓰기를 금지한다. 파일이 새롭게 생성하거나 >> 기호를 이용한 파일 내용 추가는 가능하다.
$ set -o noclobber
shift 는 위치 매개 변수를 이동할 때 사용하는 명령어다.
shift [n] 위치 매개 변수를 n 만큼 이동하는데 n이 생략되면 기본적으로 1이 적용된다. 즉 $2는 $1로 $3은 $2로 이동한다.
$ vi sh1
#!/bin/bash
while [ $# -gt 0 ]
do
echo "$# : $*"
shift
done
$ ./sh1 a b c
3 : a b c
2 : b c
1 : c
$ vi sh2
#!/bin/bash
echo $#
echo $1
echo $2
echo $3
echo $4
echo "------------------"
shift 2
echo $#
echo $1
echo $2
echo $3
echo $4
$ ./sh2 a b c d
# 매개변수의 개수
4
a
b
c
d
------
# 2 만큼 이동한 후 매개변수의 개수
2
c
d
source 는 지정한 텍스트 파일의 내용을 읽고 실행한다.
source 파일[인수]
eval 은 인자의 값을 구하는데 사용하고 만약 값이 실행 명령어와 같은 문자열인 경우 해당 명령을 실행시킨다.
$ vi ev1
#!/bin/bash
var1=10
var2=var1
eval result='$'$var2
echo $result
# 입력한 명령어를 eval을 통해 바로 실행시킨다.
$ source ./ev1
10
$ vi ev
#/bin/bash
echo "order?"
read command
eval $command
$ ./ev
order?
pwd
/root
expr 인자로 사용되는 표현식의 값을 구할 때 사용하는데 산술연산이 가능하다. 셸 상태에서 명령어로도 사용 가능하다. 다만 셸 상태에서는 연산자 사용 시 * 등을 사용할 경우 앞에 \를 붙여 사용해야 하고, 값과 연산자 사이는 반드시 한 칸 띄워야 한다.
$ expr 값 연산자 값
$ var2= `expr $var + 1`
$ expr 3 + 2
5
# * 기호는 반드시 \로 특수부호로서의 기능(all)을 없에야한다.
$ expr 3 \* 2
printf 는 shell에서만 사용 가능한 명령어로 형식화된 출력을 위해 사용한다.
printf "format string" parameter1 parameter2
# %s : 문장 출력
# %c : 한 문자 출력
# %% : % 문자 출력
$ printf "%s\n" shell
shell
# %d : 십진수 출력
$ printf "%s %d\t%s\n" "shell programming" 100 gogo
shell programming 100 gogo
true 명령어는 셸 상에서 참의 의미를 가지는 0을 반환한다. 셸 스크립트 상에서 무한루프를 만들 때 사용된다.
$ vi tr1
#!/bin/bash
while true
do
echo "over and over"
done
$vi tr2
#!/bin/bash
rm -f file
if [ -f file ]
then
else
echo "file did not exist"
fi
exit 0
$ ./tr
file did not exist
type 명령어는 주어진 인자가 셸 스크립트, 함수 , alias인지를 검사하여 출력한다.
type [option] 인자
-all : 지정한 인자 이름으로 정의된 모든 정보를 출력한다.
-path : 실행 파일이나 셸 스크립트에 한정하여 관련 정보를 출력한다.
-type : 인자 이름과 일치하는 키워드 하나만 출력한다.
$ type pushd
pushd is a shell bulitin