리눅스 서버를 자동화하여 관리하기 위한 쉘 스크립트(Shell Script) 심화 강좌를 정리합니다.
출처 : inflearn
1. 조건문(if..else.fi)
linux:/home/shkim$ if true; then
> echo true
> else
> echo false
> fi
true
linux:/home/shkim$ if true; then echo true; else echo false; fi # 한줄로도 표현 가능 줄바꿈은 세미콜론 처리.
true
if 문(IF Statement)
- style #1
if COMMANDS then OTHER COMMANDS fi
- style #2
if COMMANDS then OTHER COMMANDS fi
- style #3
if COMMANDS;then OTHER COMMANDS fi
2. [..] vs [[..]]
linux:/home/shkim$ tom="Tom janks"
linux:/home/shkim$ deniro="Robert Deniro"
linux:/home/shkim$ [ $tom > $deniro ] # 대괄호는 테스트로 사용함, [..] 안에서 >< 문자는 리다이렉트로 해석된다.
-bash: $deniro: ambiguous redirect
linux:/home/shkim$
linux:/home/shkim$ [[ $tom > $deniro ]]
linux:/home/shkim$ echo $? # $?는 직전 명령의 성공,실패를 확인할 때 사용
0 # 참
linux:/home/shkim$ [ $tom = $deniro ] # ==> [ Tom hanks = Robert Deniro ] // 공백으로 인한 에러
-bash: [: too many arguments
linux:/home/shkim$ [ "$tom" = "$deniro" ]
linux:/home/shkim$ [ "Tom hanks" = "Robert Deniro" ]
linux:/home/shkim$ [[ $tom = $deniro ]]; echo $?
1
linux:/home/shkim$ [[ $tom > $deniro ]]; echo $?
0
3. 인용부호 사용시 주의사항
linux:/home/shkim$ VAR=; if [ $VAR = "" ];then echo true; else echo false;fi
-bash: [: =: unary operator expected
false
# 위의 내용을 해석하면 다음과 같음 [ = ""]
linux:/home/shkim$ VAR=; if [ "$VAR" = "" ];then echo true; else echo false;fi
true
# 인용부호를 사용하여 해결
linux:/home/shkim$ VAR=; if [[ $VAR = "" ]];then echo true; else echo false;fi
true
# [[ ]] 테스트 부호를 입력하여 인용문자 생략 할 수 있음.
4. 비교 메타 문자들
linux:/home/shkim/a$ touch hello.txt
linux:/home/shkim/a$ if [ ! -f "hello.txt.bak" ]; then
> cp "hello.txt" "hello.txt.bak"
> fi
linux:/home/shkim/a$ ls
hello.txt hello.txt.bak
# 현재 디렉토리에 hello.txt.bak 파일이 없으면 hello.txt 파일을 hello.txt.bak 복사본 생성
# -f // 파일의 존재여부 확인 할 때 사용
Name | Description |
---|---|
-e FILE | 파일이 있는 경우 True 입니다. |
-f FILE | 파일이 일반 파일인 경우 True 입니다. |
-d FILE | 파일이 디렉터리인 경우 True 입니다. |
-h FILE | 파일이 심볼 링크인 경우 True 입니다. |
-p PIPE | 파이프가 있는 경우 True 입니다. |
-r FILE | 사용자가 파일을 읽을 수 있는 경우 True 입니다. |
-e FILE | 파일이 있는 경우 True 입니다. |
-s FILE | 파일이 존재하며 비어 있지 않은 경우 True 입니다. |
-t FD | 터미널에서 FD가 열려 있는 경우 True 입니다. |
-w FILE | 사용자가 파일을 쓸 수 있는 경우 True 입니다. |
linux:/home/shkim$ if (( $? ));
> then echo 'Please run using "bash" or "sh", but not "." or "source"' >&2;
> exit 1;
>fi
# 앞선 명령어가 비정상 실행 일 경우 실행을 무효화 시키는 스크립트.
# 어떤 경로에 파일 목록이 있는지 확인
linux:/home/shkim$ if [[ $(ls -A) ]]; then
> echo "there are files"
> else
> echo "no files found"
> fi
there are files
linux:/home/shkim/shell_cmd$ cat sleep.sh
#!/bin/bash
while true; do
sleep 1;
done
linux:/home/shkim/shell_cmd$ ps
PID TTY TIME CMD
10386 pts/0 00:00:00 bash
11304 pts/0 00:00:00 sleep.sh
11417 pts/0 00:00:00 sleep
11423 pts/0 00:00:00 ps
linux:/home/shkim/shell_cmd$ result=`ps aux | grep -i "sleep.sh" | grep -v "grep" | wc -l`
linux:/home/shkim/shell_cmd$ if [ $result -ge 1 ] # 크거나 같다 (greater and equal)
> then
> echo "script is running"
> else
> echo "script is not running"
> fi
script is running
linux:/home/shkim/shell_cmd$ ps
PID TTY TIME CMD
10386 pts/0 00:00:00 bash
11575 pts/0 00:00:00 sleep.sh
12021 pts/0 00:00:00 sleep
12027 pts/0 00:00:00 ps
linux:/home/shkim/shell_cmd$ kill 11575 12021
-bash: kill: (12021) - No such process
[1]+ Terminated ./sleep.sh
linux:/home/shkim/shell_cmd$ ps
PID TTY TIME CMD
10386 pts/0 00:00:00 bash
12077 pts/0 00:00:00 ps
linux:/home/shkim/shell_cmd$ result=`ps aux | grep -i "sleep.sh" | grep -v "grep" | wc -l`
linux:/home/shkim/shell_cmd$ if [ $result -ge 1 ]; then echo "script is runngin"; else echo "script is not running"; fi
script is not running
[ ] 을 이용한 TEST
Name | Description |
---|---|
-x FILE | 파일이 실행 가능한 경우 True 입니다. |
-O FILE | 파일이 사용자가 효과적으로 소유하는 경우 True 입니다. |
-G FILE | 파일이 그룹에 의해 효과적으로 소유되는 경우 가능한 경우 True 입니다. |
FILE -nt FILE | 첫 번째 파일이 두 번째 파일보다 최신이면 경우 True 입니다. |
FILE -ot FILE | 첫 번째 파일이 두 번째 파일보다 오래된 경우 True 입니다. |
-z STRING | 문자열이 비어 있으면 True 입니다.(길이가 0임) |
-n STRING | 문자열이 비어있지 않은 경우 True 입니다.(길이 0이 아님) |
STRING = STRING | 첫 번째 문자열이 두 번째 문자열과 동일한 경우 True 입니다. |
STRING != STRING | 첫 번째 문자열이 두 번째 문자열과 동일하지 않은 경우 True 입니다. |
STRING < STRING | 첫 번째 문자열이 두 번째 문자열보다 먼저 정렬되는 경우 True 입니다. |
STRING > STRING | 첫 번째 문자열이 두 번째 문자열 뒤에 정렬되는 경우 True 입니다. |
EXPR -a EXPR | 두 식이 모두 참이면 참입니다.(logical AND) |
EXPR -o EXPR | 두 식 중 하나가 참이면 참입니다.(logical OR) |
! EXPR | 표현식의 결과를 반전합니다.(logical NOT) |
INT -eq INT | 두 정수가 동일한 경우 True 입니다. |
INT -ne INT | 정수가 동일하지 않은 경우 True 입니다. |
INT -lt INT | 정수가 동일하지 않은 경우 True 입니다. |
INT -gt INT | 첫 번째 정수가 두 번째 정수보다 큰 경우 True 입니다. |
INT -le INT | 첫 번째 정수가 두 번째 정수보다 작거나 같으면 True 입니다. |
INT -ge INT | 첫 번째 정수가 두 번째 정수보다 크거나 같은 경우 True 입니다. |
STRING =(or ==) PATTERN | [ 과 같은 문자열 비교는 아니지만 패턴 일치가 수행됩니다. 문자열이 글로브 패턴과 일치하는 경우 Treu 입니다. |
STRING != PATTERN | [ 과 같은 문자열 비교는 아니지만 패턴 일치가 수행됩니다. 문자열이 글로브 패턴과 일치하지 않는 경우 True 입니다. |
STRING =~ REGEX | 문자열이 regex 패턴과 일차히는 경우 True 입니다. |
(EXPR) | 괄호를 사용하여 평가 우선 순위를 변경 할 수 있습니다. |
EXPR && EXPR | 테스트의 -a 연산자와 매우 유사하지만 첫 번째 표현식이 이미 거짓으로 판명되면 두 번째 표현식을 평가하지 않습니다. |
EXPR || EXPR |
테스트의 -o 연산자와 매우 유사하지만 첫 번째 표현식이 이미 사실인 경우 두 번째 표현식을 평가하지 않습니다. |
[ ]
보다는 [[ ]]
를 사용하는 것이 좋습니다.
실습(DRILL)
- 스크립트 뒤에 전달인자가 있어야 실행되는 스크립트를 작성해 보세요.
#!/bin/bash
if [ -z "$1" ] ; then
echo "usage: $0 directory"
exit
fi
echo "Have a nice day!"
6. while 루프
# 1초에 한번씩 Hello world 출력
linux:/home/shkim/a$ while true; do
> echo "Hello world"
> sleep 1
> done
Hello world
Hello world
Hello world
^C
linux:/home/shkim/a$
# 1초에 한번씩 비프음 발생
linux:/home/shkim/a$ while true; do
> echo -n -e "\a";
> sleep 1;
> done
linux:/home/shkim/a$ for no in `eval echo {0..$COUNT}`; do
> echo $no
> done
0
1
2
3
4
5
6
7
8
9
10
linux:/home/shkim/a$
linux:/home/shkim/a$ eval echo {0..$COUNT}
0 1 2 3 4 5 6 7 8 9 10
linux:/home/shkim/a$
실습(DRILL)
- 1부터 100까지 더하는 스크립트를 작성해 보세요.
#!/bin/bash
sum=0
for num in {1..100}; do
(( sum = sum+num ))
done
echo "SUM=$sum"
7. for..in 루프
linux:/home/shkim$ classroom=(desk pen note chair book)
linux:/home/shkim$ echo ${classroom[@]}
desk pen note chair book
# ! 는 배열의 인덱스 참조 / 배열의 요소를 가져오는 것이 아니고 인덱스 값을 얻어오는 방법
linux:/home/shkim$ for i in ${!classroom[@]} ; do
> if [ "${classroom[$i]}" = 'pen' ] ; then
> classroom[$i]=''
> fi
> done
linux:/home/shkim$ echo ${classroom[@]}
desk note chair book
linux:/home/shkim$ echo ${!classroom[@]}
0 1 2 3 4
실습(DRILL)
# 이미지 매직을 사용하여 locale 이미지 파일 만들기
# imagemagick 설치 후 진행
for locale in en es fr ja ko th vi zh-cn zh-tw; do convert -size 256x256 -background transparent -gravity Center -fill black -font arial.ttf -pointsize 240 label:${locale} ${locale}.png; done
8. for((;;)) 루프
linux:/home/shkim$ mystr="Hello world"
linux:/home/shkim$ for((i=0;i<${#mystr};i++)); do
> c="${mystr:$i:1}"
> echo "$c"
> done
H
e
l
l
o
w
o
r
l
d
9. 명령어(date)
linux:/home/shkim$ date +"%Y-%m-%d"
2021-10-01
linux:/home/shkim$ date +"%Y/%m/%d"
2021/10/01
linux:/home/shkim$ date +"%Y-%m-%d %r"
2021-10-01 12:30:58 AM
linux:/home/shkim$ date +"%Y-%m-%d %H:%M"
2021-10-01 00:31
linux:/home/shkim$ date +"%Y-%m-%d %H:%M %A"
2021-10-01 00:31 Friday
linux:/home/shkim$ date "+DATE: %Y-%m-%d%nTIME: %H:%M:%S"
DATE: 2021-10-01
TIME: 00:32:39
실습(DRILL)
# command line 시계만들기
#!/bin/bash
while true; do
TIME=$(date "+DATE: %Y-%m-%d %H:%M:%S")
echo -ne "$TIME\r"
sleep 1
done
10. 루프문과 glob
- 오류 1
linux:/home/shkim/mydir$ ll
total 0
-rw-rw-r-- 1 shkim shkim 0 Oct 17 21:18 For Whom the Bell Tolls.mp3
-rw-rw-r-- 1 shkim shkim 0 Oct 17 21:19 Gone With the Wind.mp3
-rw-rw-r-- 1 shkim shkim 0 Oct 17 21:19 old man and ?the sea.mp3
linux:/home/shkim/mydir$ for file in $(ls *.mp3);do
> rm "$file"
> done
rm: cannot remove ‘For’: No such file or directory
rm: cannot remove ‘Whom’: No such file or directory
rm: cannot remove ‘the’: No such file or directory
rm: cannot remove ‘Bell’: No such file or directory
rm: cannot remove ‘Tolls.mp3’: No such file or directory
rm: cannot remove ‘Gone’: No such file or directory
rm: cannot remove ‘With’: No such file or directory
rm: cannot remove ‘the’: No such file or directory
rm: cannot remove ‘Wind.mp3’: No such file or directory
rm: cannot remove ‘old’: No such file or directory
rm: cannot remove ‘man’: No such file or directory
rm: cannot remove ‘and’: No such file or directory
rm: cannot remove ‘?the’: No such file or directory
rm: cannot remove ‘sea.mp3’: No such file or directory
# 실패원인 : 파일에 띄워쓰기가 들어가 각각의 분리된 값이 $file 변수에 대입됨.
- 오류 2
linux:/home/shkim/mydir$ for file in "$(ls *.mp3)"; do
> rm "$file";
> done
rm: cannot remove ‘For Whom the Bell Tolls.mp3\nGone With the Wind.mp3\nold man and ?the sea.mp3’: No such file or directory
# 실패원인 : 전체파일이 하나의 문자열로 취급됨.
- 성공
linux:/home/shkim/mydir$ ls
For Whom the Bell Tolls.mp3 Gone With the Wind.mp3 old man and ?the sea.mp3
linux:/home/shkim/mydir$ for file in *.mp3 ; do # 인용문자가 필요치 않음.
> rm "$file" # 인용문자를 꼭 해줘야한다.
> done
linux:/home/shkim/mydir$ ls
linux:/home/shkim/mydir$
# 쉴스크립트에서 인용부호를 사용해야 할 때와 사용하지 않아야 할때를 구분할 줄 아는 것은 매우 중요합니다.
linux:/home/shkim/mydir$ for file in "*.mp3" ; do
> rm "$file"
> done
rm: cannot remove ‘*.mp3’: No such file or directory
linux:/home/shkim/mydir$ ls
# * 를 사용할 때는 " " 인용 부호를 사용하면 안된다.
실습(DRILL)
- for 문을 이용하여 /shell_cmd/images 파일의 백업본을 만들어보세요.
for file in *; do cp ${file}{,.bak}; done
11. 명령어(seq)
linux:/home/shkim$ seq 1 10
1
2
3
4
5
6
7
8
9
10
linux:/home/shkim$ seq 0 2 10
0
2
4
6
8
10
linux:/home/shkim$ seq -s, 1 1 10
1,2,3,4,5,6,7,8,9,10
linux:/home/shkim$ seq -s, 1 2 10
1,3,5,7,9
linux:/home/shkim$ seq 10 -1 1
10
9
8
7
6
5
4
3
2
1
실습(DRILL)
: seq 와 printf 그리고 for을 이용하여 다음처럼 출력하도록 프로그램 하세요.
001 002 003 004 005 006 007 008 009 010
#!/bin/bash
for num in $(seq 1 10) ; do
printf "%03d\t" $num
done
echo
12. case
linux:/home/shkim$ read -p "Enter any string: "
Enter any string: abc
linux:/home/shkim$ case $REPLY in
> +([[:digit:]]) ) echo "digits" ;;
> *) echo "not digits" ;;
> esac
not digits
linux:/home/shkim$ read -p "Enter any string: "
Enter any string: 123
linux:/home/shkim$ case $REPLY in +([[:digit:]]) ) echo "digits" ;; *) echo "not digits" ;; esac
digits
실습(DRILL)
: 사용자에게 Y/N 입력을 받아 종료하는 스크립트를 만들어보세요.
#!/bin/bash
read -s -n 1 -p "You really want to exit ?" response
case $response in
y|Y) echo "YES";;
n|N) echo "NO";;
*) kill -SIGKILL $$;;
esac
13. getopts
- 하이픈 (-)으로 전달인자를 전달받아 처리하는 방식
linux:/home/shkim/shell_cmd$ vi Anaconda2-2.4.1-MacOSX-x86_64.sh
while getopts "bfhp:z:" x; do
case "$x" in
h)
echo "usage: $0 [options]
Installs Anaconda2 2.4.1
-b run install in batch mode (without manual intervention),
it is expected the license terms are agreed upon
-f no error if install prefix already exists
-h print this help message and exit
-p PREFIX install prefix, defaults to $PREFIX
linux:/home/shkim/shell_cmd$ Anaconda2-2.4.1-MacOSX-x86_64.sh -b -f # 이런 식으로 사용하는 것을 의미
14. select
linux:/home/shkim/shell_cmd$ movies=("Avengers" "Matrix" "Titanic")
linux:/home/shkim/shell_cmd$ PS3="Please select your favorite movie: "
linux:/home/shkim/shell_cmd$ select movie in ${movies[@]}
> do
> echo "$movie selected"
> done
1) Avengers
2) Matrix
3) Titanic
Please select your favorite movie: 1
Avengers selected
Please select your favorite movie: 2
Matrix selected
Please select your favorite movie: 3
Titanic selected
Please select your favorite movie: ^C
linux:/home/shkim/shell_cmd$
#!/bin/bash
movies=("Avengers" "Matrix" "Titanic", "None")
PS3="Please select your favorite movie: "
select movie in ${movies[@]} ; do
case $movie in
"None") echo "My favorite movie is not on the list. quit ";break;;
*) echo "$movie selected";;
esac
done
inux:/home/shkim/shell_cmd$ ./select.sh
1) Avengers
2) Matrix
3) Titanic,
4) none
Please select your favorite movie: 1
Avengers selected
Please select your favorite movie: 2
Matrix selected
Please select your favorite movie: 3
Titanic, selected
Please select your favorite movie: 4
none selected
Please select your favorite movie: 5
selected
Please select your favorite movie: ^C
PREVIOUS섹션 4. 정규 표현식과 검색도구
NEXT섹션 6. 배열