섹션 3. 쉘 스크립트(ShellScript)

 

리눅스 서버를 자동화하여 관리하기 위한 쉘 스크립트(Shell Script) 심화 강좌를 정리합니다.
출처 : inflearn

1. 쉘 스크립트란 무엇입니까?

  • 쉘(Shell)은 명령 인터프리터(Command interpreter)입니다.
  • 사용자가 운영체제에 대화식(interactively)으로 명령을 내리거나,
    명령을 일괄(batch)적으로 실행 할 수 있는 기능을 제공하는 응용프로그램입니다.
  • 쉘은 사용자가 시스템과 대화 할 수 있는 방법이라고 생각하십시오.
  1. Kernel
    • Core of the OS
    • Allocates time and memory to programs
  2. Shell
    • Outer layer of OS
    • Interacts with user
    • Sends requests to kernel
  3. 인기있는 쉘의 종류
    • sh : Thompson Shell(1971)
    • sh : Bourne Shell(1977)
    • csh : C Shell(1979)
    • tcsh : Tabbed C Shell(1979)
    • ksh : Korn Shell(1982)
    • bash : Bourne-Again Shell(1987)
    • zsh : Z Shell(1990)
    • dash : Dash Shell(2002)

2. 스크립트 작성방법

linux:/home/shkim/shell_cmd$ vi helloworld.sh
#!/bin/bash # 쉬뱅, 해쉬뱅 이라고 부른다.
echo hello world

#!/bin/env bash # 이렇게 해도 정상 동작 함.
echo hello world

linux:/home/shkim/shell_cmd$ ps
  PID TTY          TIME CMD
 6727 pts/0    00:00:00 bash #bash는 항상 background로 실행되고 있음.
 6802 pts/0    00:00:00 ps

3. DOS 스타일의 줄끝

linux:/home/shkim/shell_cmd$ echo -e 'hello\n\n\n'
hello



linux:/home/shkim/shell_cmd$
  • 리눅스에서는 줄바꿈시 10 0A : LF Line Feed 만 사용되지만,
  • 윈도우에서는 줄바꿈시 10 0A : LF, 13 0D CR Carriage Return LF,CR 2개가 사용됨.아래참고
  • 파일 수정시 리눅스 공식 VIM 사용을 추천함.
linux:/home/shkim/shell_cmd$ hexdump -C hello.txt
00000000  68 65 6c 6c 6f 0a                                 |hello.|
00000006
linux:/home/shkim/shell_cmd$ hexdump -C hello_ms.txt
00000000  68 65 6c 6c 6f 0a 0d                              |hello..|
00000007
linux:/home/shkim/shell_cmd$

4. 스크립트의 실행방법 4가지

linux:/home/shkim/shell_cmd$ ./helloworld.sh
hello world
linux:/home/shkim/shell_cmd$ bash helloworld.sh
hello world
linux:/home/shkim/shell_cmd$ source helloworld.sh
hello world
linux:/home/shkim/shell_cmd$ . helloworld.sh
hello world

5. 특수문자 종류 미리보기

  • 공백(White Space) :
    탭(tab), 줄 바꿈(newline), 세로 탭, 양식 공급(form feed),
    캘리지 리턴(Carriage Return) 또는 공백(White Space)입니다.

    Bash는 공백을 사용하여 단어의 시작과 끝을 결정합니다. 사용자가 명령어를 입력할 시,
    첫번째 단어는 명령이름이며, 추가단어는 해당명령에 대한 인수가 됩니다.

  • 확장(Expansion) : ${}, $(), $(())
    다양한 유형의 확장(Extenstion)을 도입합니다.
    1. parameter expansion (파라미터 확장) : $var or ${var}
    2. command substitution (명령 대체) : $(command)
    3. arithmetic expansion (산술 확장) : $((expression))
  • 큰 따옴표(Double Quotes) : “ “
    그 안의 텍스트가 여러 단어나 인수로 분리되지 않도록 보호합니다.

    큰 따옴표 내의 문자들을 대체(Substitution)하는 것이 가능합니다.
    \(백슬러시), $(달러),``(백틱)를 제외한 대부분의 다른 특수 문자의 의미는 억제됩니다.(즉 일반문자로 해석됩니다.)

  • 작은 따옴표(Single Quote) : ‘ ‘
    문자 그대로의 의미를 갖도록 텍스트를 보호하십시오.

    _모든 특수문자의 해석이 방지됩니다.(인용부호 안의 문자열 내용을 Bash가 해석하지 않습니다.)
    특수문자가 그대로 전달되고 여러 단어가 분할되지 않습니다.

linux:/home/shkim/shell_cmd$ echo "사용법: `basename $HOME` filename"
사용법: shkim filename
linux:/home/shkim/shell_cmd$ echo '사용법: `basename $HOME` filename'
사용법: `basename $HOME` filename
  • 탈출(Escape) : \
    다음 문자가 특수문자로 해석되는 것을 방지합니다.

    큰 따옴표 안에서 작동하며 작은 따옴표로는 일반적으로 무시됩니다.

linux:/home/shkim/shell_cmd$ echo "사용법:\`basename \$HOME\` filename"
사용법:`basename $HOME` filename
  • 주석(Comment) : #
    ’#’ 문자의 도입은 그 행의 끝까지 모두 주석으로 처리됩니다.

    코멘트는 설명의 주석이며 쉘에 의해 처리되지 않습니다.

  • 테스트(Test) : [[ ]]
    조건부 표현식이 “true”인지 “false”인지를 결정하기 위한 조건식의 평가

    테스트는 Bash에서 여러 조건을 평가하는데 사용됩니다.

  • 부정하다(Negate) : !
    테스트나 종료 상태를 무효화하거나 되돌리기 위해 사용됩니다.
  • 방향재지정(Redirection) : > <
    명령의 출력또는 입력을 재 지정합니다.
linux:/home/shkim$ rm dir1 2>/dev/null # 에러 메세지는 무시
  • 파이프(PIPE) : |
    초기 명령의 출력을 2차 명령의 입력으로 재 지정합니다.
linux:/home/shkim$ echo "Hello World" | grep -o World
World
linux:/home/shkim$ echo "60+1" | bc
61
  • 명령 분리자(Command Separator) : ;
    같은 줄에 있는 여러 명령을 구분하는데 사용됩니다.
linux:/home/shkim/shell_cmd$ sum=0;while read num ; do sum=$(($sum + $num)); done < nums.txt; echo $sum
5050
  • 인라인 그룹(Inline Group) : { }
    중괄호 안의 명령은 마치 하나의 명령처럼 취급됩니다.

    _Bash구문이 하나의 명령만을 필요로 하고, 함수의 사용은 피하고 싶을 때, 이것을 사용하는 것이 편리합니다.

linux:/home/shkim$ { local v1; v1=123; }
-bash: local: can only be used in a function
  • 서브 쉘 그룹(SubShell Group)
    위와 비슷하지만 내부 명령이 서브 쉘에서 실행되는 경우

    명령이 부작용을 일으키는 경우 샌드박스처럼 많이 사용됩니다.(변수 변경하기 같은 경우)
    현재의 쉘에는 영향을 주지 않습니다.

linux:/home/shkim/shell_cmd$ u2dos() (set -f; IFS=''; printf '%s\r\n' $(cat "$1"))
linux:/home/shkim/shell_cmd$ u2dos helloworld.sh
#!/usr/bin/env bash
echo hello world
  • 산술 표현식(Arithmetic Expression) : (( ))
    산술 표현식에서 +, -, *, / 같은 문자는 계산에 사용되는 수학 연산자 입니다.

    그것들은 다음과 같은 변수 할당에 사용할 수 있습니다.((a=1+4), 테스트에도 사용합니다 if((a<b))

  • 산술 확장(Arithmetic Expansion) : $(( ))
    위와 유사하지만 표현식은 산술 계산 결과로 대체됩니다.
linux:/home/shkim/shell_cmd$ echo "The average is $(( (a+b)/2))"
The average is 0
  • 홈 디렉토리(Home Directory) : ~
    ~(tild)는 홈 디렉토리를 나타냅니다.

    그 다음에 \ 이 올때 ~는 현재 사용자의 홈 디렉토리를 나타냅니다.
    또는 사용자 이름을 지정해야 합니다.

linux:/etc$ ~/shell_cmd/helloworld.sh
hello world

6. 쉘 변수

linux:/home/shkim$ animal=tiger # 변수 대입, 공백이 있으면 안됨.

linux:/home/shkim$ color=white
linux:/home/shkim$ echo "tiger's color is $color"

7. 파라미터 대체와 인용부호

linux:/home/shkim/mydir$ touch "The old man and the sea.mp3"
linux:/home/shkim/mydir$ echo $book
The old man and the sea.mp3
linux:/home/shkim/mydir$ ls
The old man and the sea.mp3
linux:/home/shkim/mydir$ rm $book
rm: cannot remove ‘The’: 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
linux:/home/shkim/mydir$ rm "$book"
linux:/home/shkim/mydir$ ls

linux:/home/shkim/mydir$ animal=Tiger; color=Red
linux:/home/shkim/mydir$ echo "$animals $colors"

linux:/home/shkim/mydir$ echo ${animal}s vs. ${color}s
Tigers vs. Reds

linux:/home/shkim/mydir$ echo "${animal}s vs. ${color}s" #인용부호로 확실하게 하는게 더 좋다.
Tigers vs. Reds

8. 특수 매개 변수

linux:/home/shkim$ vi whereis.sh

#!/bin/bash
DIRECTORY=`dirname $0`
echo $DIRECTORY
linux:/home/shkim$ ./where.sh


linux:/home/shkim$ /home/shkim/where.sh
/home/shkim

linux:/home/shkim$ vi whois.sh
#!/bin/bash
name=$1
email=$2
all=$*

echo "your name is $name"
echo "your email is $email"
echo "* is $all"

linux:/home/shkim$ ./whois.sh
your name is
your email is
* is

linux:/home/shkim$ ./whois.sh shkim shkim@gmail.com #전달인자가 있어야 함.
your name is shkim
your email is shkim@gmail.com
* is shkim shkim@gmail.com


특수매개변수

Name Usage Description
0 $0 스크립트의 이름 또는 경로를 포함합니다.
1 2 etc $1 etc 위치 매개 변수에는 현재 스크립트 또는 함수에 전달된 인수가 포함됩니다.
* ”$*” 모든 위치 매개 변수의 모든 단어로 확장됩니다. 큰 따옴표를 붙이면, IFS 변수의 첫번째 문자로 분리 된 모든 문자열을 포함하는 단일 문자열로 확장됩니다.
@ $@ 모든 위치 매개변수의 모든 단어로 확장됩니다. 큰 따옴표로 묶어서 개별 단어로 모두 목록으로 확장합니다.
# $# 위치 매개 변수의 수로 확장 됩니다.
? $? 가장 최근에 완료한 포 그라운드 명령의 종료 코드로 확장합니다.
$ $$ 현재 쉘의 PID(프로세스ID)으로 확장됩니다.
! $! 백그라운드에서 가장 최근에 실행된 명령의 PID로 확장합니다.
_ $_ 실행된 마지막 명령의 마지막 인수로 확장합니다.
linux:/home/shkim$ vi whois.sh
#!/bin/bash
name=$1
email=$2
all=$*

if [ $# -eq 0 ] # 전달인자의 개수가 0 이면~
then
  echo "No arguments supplied"
fi

echo "your name is $name"
echo "your email is $email"
echo "* is $all"

linux:/home/shkim$ ./whois.sh
No arguments supplied
your name is
your email is
* is

9. 환경변수

linux:/home/shkim$ echo "USER ID : $UID"
USER ID : 1001

linux:/home/shkim$ vi euid.sh
#!/bin/bash

if [ "$EUID" -ne 0 ]; then # Effected User ID
    echo "run as root"
    exit
fi  
echo hello

linux:/home/shkim$ chmod +x euid.sh
linux:/home/shkim$ ./euid.sh
run as root

linux:/home/shkim$ sudo ./euid.sh
hello
linux:/home/shkim$ echo "$RANDOM"
5502
linux:/home/shkim$ echo "$RANDOM"
11364
linux:/home/shkim$ echo "$RANDOM"
2396

10. declare

linux:/home/shkim$ declare -a alnum=(a1 b1 c1 d1 e1 f1) # -a 배열 옵션
linux:/home/shkim$ echo ${alnum[2]}
c1

linux:/home/shkim$ declare -i inum=78 #정수형 변수
linux:/home/shkim$ inum=inum+1
linux:/home/shkim$ echo $inum
79

linux:/home/shkim$ num=78 # 7과 8이라는 문자로 인식됨.
linux:/home/shkim$ num=num+1
linux:/home/shkim$ echo $num
num+1

linux:/home/shkim$ declare -r rPi=3.14 # -r 읽기전용 번수
linux:/home/shkim$ rPi=312 # 초기화 불가함.
-bash: rPi: readonly variable

linux:/home/shkim$ declare -x xpath="${HOME}/Desktop/mydir" # 변수를 export 하기
linux:/home/shkim$ export XPATH="${HOME}/Desktop/mydir" # 위와 동일함.

11. 매개변수 확장(PE)

linux:/home/shkim$ testString="That that is is that that is not is not"
linux:/home/shkim$ echo ${#testString} # 문자열의 수를 셀 때
39

linux:/home/shkim$ echo ${testString:0} # 원하는 문자열의 시작점을 지정할 수 있다.
That that is is that that is not is not

linux:/home/shkim$ echo ${testString:1}
hat that is is that that is not is not

linux:/home/shkim$ echo ${testString:3}
t that is is that that is not is not

linux:/home/shkim$ echo ${testString:3:3} #4번째 문자열 부터 3 chracter
t t

linux:/home/shkim$ echo ${testString#T*is} # 대문자 T로 시작하고 is로 끝나는 문자열 제거
is that that is not is not

linux:/home/shkim$ echo ${testString##T*is} # 대문자 T로 시작하고 is로 큰 집합 제거
not

linux:/home/shkim$ echo ${testString%is*not} # 문장 뒤부터 검색해서 소문자 is로 시작해서 not 으로 끝나는 문자열제거
That that is is that that is not

linux:/home/shkim$ echo ${testString%%is*not} # 문장 뒤부터 검색해서 소문자 is로 시작해서 not 으로 끝나는 큰 집합의 문자열제거
That that

linux:/home/shkim$ echo ${testString//that} # 해당 단어와 일치하는 문자 제거
That is is is not is not

linux:/home/shkim$ echo ${testString/that/this} # 매칭이 일어난 1개의 단어와 치환
That this is is that that is not is not

linux:/home/shkim$ echo ${testString/[tT]hat/this} # 대소문자 상관없이 that 단어를 this로 치환 / 처음매치된 1개 단어만 치환됨.
this that is is that that is not is not

linux:/home/shkim$ echo ${testString//[tT]hat/this} # 대소문자 상관없이 that 단어 --> this로 모두 치환
this this is is this this is not is not

linux:/home/shkim$ echo ${testString/#That/this}
this that is is that that is not is not

linux:/home/shkim$ echo ${testString/%not/NO} # 마지막 문자에서 매칭 후 치환
That that is is that that is not is NO

12. glob 패턴

linux:/home/shkim$ cd /
linux:/$ echo *
bin boot dev etc home lib lib64 local media mnt opt proc root run sbin srv sys tmp usr var

linux:/$ echo ??? # 3글자의 파일,디렉토리 출력
bin dev etc lib mnt opt run srv sys tmp usr var

linux:/$ echo ???? # 4글자 파일,디렉토 출력
boot home proc root sbin

linux:/$ echo b?? # b로시작하는 3글자 출력
bin

linux:/$ echo [abcd]* # a,b,c,d 로 시작하는 모든 파일이나 디렉토리 출력
bin boot dev

linux:/$ echo [a-d]* # a,b,c,d 로 시작하는 모든 파일이나 디렉토리 출력
bin boot dev

13. 명령어(tr)

  • tr : 지정한 문자를 바꾸어주거나 삭제하기 위한 명령 줄 유틸리티.
  • «< : 히어스트링(Here String)
  • [::] : POSIX chracter sets
linux:/$ tr abcdefghijklmnopqrstuvwxyz ZABCDEFGHIJKLMNOPQRSTUVWXY <<< "Hello World"
HDKKN WNQKC # 해당 문자열에 해당하는 문자는 새로운 패턴으로 치환되어 출력됨.

linux:/$ tr [:lower:] [:upper:] <<< "Hello World"
HELLO WORLD # 모든 문자열을 대문자로

linux:/$ tr [:space:] '\t' <<< "Hello World" # 공백을 tab 으로 치환
Hello   World   linux:/$

linux:/$ tr -s [:space:] <<< "Hello       World" #-s : Squeeze 반복되는 글자를 1글자로 축약
Hello World

linux:/$ tr -d [:space:] <<< "Hello      World" # 모든 공백문자 제거
HelloWorld

linux:/$ tr -cd [:space:] <<< "Hello      World" # -c complement 지정한 글자를 제외한 다른글자를 뜻함.

linux:/$       

14. 명령어(cut)

linux:/home/shkim/shell_cmd$ cat fruits.txt
grapes
orange
tomato
strawberry
apple

linux:/home/shkim/shell_cmd$ cut -c2 fruits.txt # 각행의 2번째 글자 출력
r
r
o
t
p

linux:/home/shkim/shell_cmd$ cut -c1-3 fruits.txt #각행 1~3번째 글자 출력
gra
ora
tom
str
app

linux:/home/shkim/shell_cmd$ cut -c3- fruits.txt # 각행 3~마지막 글자 출력
apes
ange
mato
rawberry
ple

linux:/home/shkim/shell_cmd$ cut -c1-5 fruits.txt
grape
orang
tomat
straw
apple

linux:/home/shkim/shell_cmd$ cut -d':' -f1 /etc/passwd # 첫번째 필드 출력
root
bin
daemon
adm
lp
sync
shutdown
halt
mail
operator
games
ftp
nobody
systemd-network
dbus
rpc
libstoragemgmt
sshd
rpcuser
nfsnobody
ec2-instance-connect
postfix
chrony
rngd
tcpdump
shkim

linux:/home/shkim/shell_cmd$ cut -d':' -f1,6 /etc/passwd #첫번째,여섯번째 필드 출력
root:/root
bin:/bin
daemon:/sbin
adm:/var/adm
lp:/var/spool/lpd
sync:/sbin
shutdown:/sbin
halt:/sbin
mail:/var/spool/mail
operator:/root
games:/usr/games
ftp:/var/ftp
nobody:/
systemd-network:/
dbus:/
rpc:/var/lib/rpcbind
libstoragemgmt:/var/run/lsm
sshd:/var/empty/sshd
rpcuser:/var/lib/nfs
nfsnobody:/var/lib/nfs
ec2-instance-connect:/home/ec2-instance-connect
postfix:/var/spool/postfix
chrony:/var/lib/chrony
rngd:/var/lib/rngd
tcpdump:/

linux:/home/shkim$ ifconfig
eth0: flags=4163<UP,BROADCAST,RUNNING,MULTICAST>  mtu 9001
        inet 172.16.1.166  netmask 255.255.255.0  broadcast 172.16.1.255
        inet6 fe80::9b:f8ff:fe5e:e306  prefixlen 64  scopeid 0x20<link>
        ether 02:9b:f8:5e:e3:06  txqueuelen 1000  (Ethernet)
        RX packets 276655  bytes 140617913 (134.1 MiB)
        RX errors 0  dropped 0  overruns 0  frame 0
        TX packets 200482  bytes 16342086 (15.5 MiB)
        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0

linux:/home/shkim$ ifconfig eth0 | grep 'inet' | cut -d: -f2 | awk '{print $2}'
172.16.1.166

15. 확장 glob

  • 확장 glob는 기본적으로 disable 되어있어 사용시 enable 하고 사용해야 합니다.
linux:/home/shkim/shell_cmd/images$ ls
Balloon.jpg  glob.gif           settings_up.png    smaller.tiff
Candy.jpg    settings_down.png  shadingimage.tiff

linux:/home/shkim/shell_cmd/images$ echo *jpg *bmp
Balloon.jpg Candy.jpg *bmp

linux:/home/shkim/shell_cmd/images$ shopt -s extglob # 확장 glob 사용

linux:/home/shkim/shell_cmd/images$ echo !(*jpg|*bmp) # jpg,bmp 를 제외한 파일 출력
glob.gif settings_down.png settings_up.png shadingimage.tiff smaller.tiff

linux:/home/shkim/shell_cmd/images$ echo @(*jpg|*bmp) # @는 or의 기능
Balloon.jpg Candy.jpg
  • 확장된 글로브(Extended Globs)
    이들은 globs에서 사용 할 수 있는 메타 문자입니다.
Name Description
?(list) 주어진 패턴의 0번 또는 1번 일치 시킵니다.
*(list) 주어진 패턴의 0회 이상 일치
+(list) 주어진 패턴의 하나 이상의 일치와 일치합니다.
@(list) 주어진 패턴 중 하나와 일치합니다.
!(list) 주어진 패턴을 제외한 모든 것을 일치시킵니다.

16. 쉘 스크립트 문법 검사 도구

  • 쉘 스크립트 정적 분석 도구로 유용한 웹사이트
  • 스크립트 오류검사 사이트 : https://shellcheck.net

17. 컬러(color)텍스트

linux:/home/shkim$ echo -e '\033[34;40mNew Color Prompt\033[0m'
New Color Prompt # 실제로는 파란색으로 나옴

linux:/home/shkim$ echo -e '\033[37;41mNew Color Prompt\033[0m'
New Color Prompt

linux:/home/shkim$ echo -e '\033[1;37;41mNew Color Prompt\033[0m'
New Color Prompt
  • 사용방법
'\033[1;37;44m COLOR \033[0m'
#\033:이스케이프, 1:스타일, 37:전경폰트색, 44m:배경폰트색
  • COLORED TEXT(ANSI)