Shell Scripting 정리

목표

Bash Shell Script를 사용할 일이 있어서 급하게 Review하고자 한다.

여기나오는 내용은 educative Shell for programer와 Mastering Linux Shell Scripting에 있는 내용을 정리하였다.

https://github.com/PacktPublishing/Mastering-Linux-Shell-Scripting-Second-Edition

 

기초

Default Shell 찾기

~$ ps $$
    PID TTY      STAT   TIME COMMAND
  10374 pts/0    Ss     0:00 -bash
  
  $ echo $SHELL
/bin/bash

Shell 종류

  • Bash Shell : Bourne shell, 가장 기초 쉘
  • Ksh shell : Korn shell, bash shell과 호환 됨
  • Csh : C언어로 만들어진 쉘

 

Shell 변경하기

$ chsh -s /bin/bash

 

Command Line 단축키

단축키 행동
Ctrl + C 현 실행 중인 Task를 종료한다
Ctrl + L 화면을 clear 한다
Ctrl + w  커서의 마지막 한문장을 삭제한다
$ aaa bbb ddd
에서 bbb 앞에서 단축키를 입력하면 aaa가 사라진다
$ bbb ddd
tab 자동완성
Ctrl + R 내가 앞서 사용했던 history에서 찾는 단어와 유사성이 있는 실행문을 찾아 준다

(reverse-i-search)`kaf': docker-compose -f docker-compose-kafka-triplet.yml down
Ctrl + U 한라인을 모두 삭제 한다

 

Variables Type

Shell variables

쉘이 정상적인 작동을 하게끔 사전 정의된 Local Variable이다.

쉘이 시작하면서 로드하게 된다.

$shell_var

 

Local Variables

하나의 쉡 내부에서만 작동하는 Variable. 다른 쉘에 영향을 주지 않는다.

사용자가 해당 쉘에서만 순간적으로 사용하게 하기 위해 사용된다.

$ MY_VALUE=hello
$ echo $MY_VALUE
hello

 

 

Environment Variable

모든 쉘에서 접근 가능한 varialbe이다.

Bash 에서는 export 를 사용해서 정의하게 된다. 대표적으로 PATH 가 있다.

$ cat .profile 

# if running bash
if [ -n "$BASH_VERSION" ]; then
    # include .bashrc if it exists
    if [ -f "$HOME/.bashrc" ]; then
        . "$HOME/.bashrc"
    fi
fi

# set PATH so it includes user's private bin if it exists
if [ -d "$HOME/bin" ] ; then
    PATH="$HOME/bin:$PATH"
fi

# set PATH so it includes user's private bin if it exists
if [ -d "$HOME/.local/bin" ] ; then
    PATH="$HOME/.local/bin:$PATH"
fi

 

기타

$USER
$ echo $USER
theyoung
$HOSTNAME
$ echo $HOSTNAME
theyoung-MS-7D77
$$
$ echo $$
10374
현 스크립트 ID
$0
$ echo $0
-bash

현 실행중인 스크립트의 이름
$1~9
스크립트 실행시 입력된 아규먼트

 

Variable setup 방법

$ VAR_NAME=HELLO
$ echo "my name is $VAR_NAME !"
my name is HELLO !
$ echo "my name is ${VAR_NAME} !"
my name is HELLO !

주의 해야할 것은 Uppercase는 일반적으로 Environment Variable로 많이 사용한다

$ 또는 ${} 를 이용해서 value를 확인할 수 있다.

Space가 필요하다면 " " 를 해줘야 한다

 

Shell Scripting

Hello world

#!/bin/bash
echo "Hello World"
exit 0
/script$ vi hello.sh
/script$ chmod +x hello.sh 
/script$ ./hello.sh 
Hello World
  • #!/bin/bash : 쉬뱅, '#' 심볼 이후에 스크립트가 시작됨을 system에게 알려줌
  • echo : standard 아웃풋 
  • exit 0 : 종료, 0이외의 값은 오류를 뜻함
/script$ echo $?
0

마지막 스크립트 실행의 결과가 0임을 나타냄

  • command1 || command2 : command1이 실패할 경우 command 2가 실행됨. 그외 실행 안됨
#!/bin/bash
echo "Hello New World"
exit 0

위와 같은 shell이 있을 경우

./hello.sh || ./hello2.sh 
Hello World

앞에 있는 쉘만 실행 한다.

만약에 앞쉘을 1로 바꾸면

~/script$ ./hello.sh || ./hello2.sh 
Hello World
Hello New World

command2가 실행 된것을 확인 할 수 있다.

 

Arguments

#!/bin/bash
echo "Hello New World $1, count $#, all the arguments $*"
exit 0
/script$ ./hello2.sh ar1 ar2 ar3 ar4
Hello New World ar1, count 4, all the arguments ar1 ar2 ar3 ar4
  • $숫자 : 아규먼트 값
  • $# : 아규먼트 카운트
  • $* : 모든 아규먼트

 

Defined Variables

#!/bin/bash
name="superman"
job="hero"
echo "Hello New World $1, count $#, all the arguments $*"
echo "name ${name}, job ${job}"
exit 0
~/script$ ./hello2.sh 
Hello New World , count 0, all the arguments 
name superman, job hero

space를 중간에 넣지 말것!!

ex) name = "superman"

List Variables

#!/bin/bash
name="superman"
job="hero"
list=(1 2 3 4 5)
echo "Hello New World $1, count $#, all the arguments $*"
echo "name ${name}, job ${job}"
echo "${list[*]}"
unset list[0]
echo "${list[*]}"
exit 0
~/script$ ./hello2.sh 
Hello New World , count 0, all the arguments 
name superman, job hero
1 2 3 4 5
2 3 4 5
  • unset은 리스트의 특정 index를 삭제함

 

Environment variables

printenv로 환경변수 값을 확인할 수 있음

~/script$ printenv
SHELL=/bin/bash
LANGUAGE=ko:en
[omitted]
SSH_TTY=/dev/pts/2
LC_NUMERIC=ko_KR.UTF-8
_=/usr/bin/printenv
OLDPWD=/home/theyoung

 

export (변수 scope)

각 스크립트간 로컬 variable은 공유되지 않음. 

export활용해서 공유 가능함

단, 실행 스크립트내에서 다른 스크립트를 호출한 경우에 한함

참고로 hello2.sh에서 name정의 부분은 삭제 하였다.
#!/bin/bash
echo "Hello World"
name="spider man"
export name
./hello2.sh
~/script$ ./hello.sh 
Hello World
Hello New World , count 0, all the arguments 
name spider man, job hero
1 2 3 4 5
2 3 4 5

아주 혹시라도 hello2.sh에 name 정의 부분을 안 삭제 한다고 해도 원래 hello.sh의 name은 해당 프로세스 내에서 수정되지는 않는다.

bash command 실행

#!/bin/bash
cur_dir=`pwd`
echo $cur_dir

iam=$(whoami)
echo $iam

스크립트 내에서 command를 실행하는 방법은 2가지가 있다

  • ` 백틱
  • $() : 중괄호
/script$ ./command.sh 
/script
theyoung

 

Debugmode script 실행

/script$ bash -x ./command.sh
++ pwd
+ cur_dir=/home/theyoung/script
+ echo /home/theyoung/script
/home/theyoung/script
++ whoami
+ iam=theyoung
+ echo theyoung
theyoung

 

사용자 입력 read

#!/bin/bash
echo -n "Your name : "
read
echo "My name is $REPLY"
~/script$ ./read.sh 
Your name : steven 
My name is steven

$REPLY 를 통해서 입력 결과를 표시함

command line 을 통한 입력

/script$ read -p "Your name is " name
Your name is steven
/script$ echo $name
steven

read 명령어를 통해서 name 변수에 이름이 입력 된것을 확인 가능함

#!/bin/bash
echo -n "Your name : "
read
echo "My name is $REPLY"

read -p "Your ages : " age 
echo "My ages is $age"
/script$ ./read.sh 
Your name : steven
My name is steven
Your ages : 43
My ages is 43
read -n1 을 아규먼트로 넣을 경우 1회의 키만 받는 다는 의미임
read -sn1 에 s를 넣을 경우 입력값이 보이지 않게 됨

 

while-case

#!/bin/bash
while [ -n "$1" ]
do
    case "$1" in
    -f) echo "--first option selected";;
    -s) echo "--second option selected";;
    -t) echo "--third option selected";;
    *) echo "no option selected";;
    esac
shift
done

while [ 비교문 ]

do

done

비교문에서 "-n"은 not empty string이라는 의미이다

case 비교대상 in

*)

esac

in 아래에 비교 대상을 직접적으로 )로 표시 하면 된다.
shift는 arguments를 하나씩 좌측으로 shipt해서 pop하게 된다.

 

for 순환

#!/bin/bash
num=1
for param in $@
do
    echo "#$num: $param"

    num=$(( $num + 1 ))
done

for variable in range

do

done

으로 이루어짐 여기서 $@는 모든 array라는 의미로 foreach 형태로 하나씩 space단위로 값을 빼어냄

num=은 variable을 재정의 하는 형태이고

$()를 통해서 command를 실행 시킴

그 내부에 ()가 하나더 있는데 'Numeric calculations'이라고 함 

./for.sh a b c d e f g
#1: a
#2: b
#3: c
#4: d
#5: e
#6: f
#7: g

 

Condition Test

~/script$ test $PWD == $HOME
/script$ echo $?
1

test를 통해서 현재 있는 위치와 HOME의 path가 같은지를 확인했다.

0이외의 결과는 error임으로 위는 error를 뜻한다

#!/bin/bash
echo "Your basename is $(basename $0)"
test -z $1 || echo "Hello $1"
exit 0

test -z $1은 $1이 empty string일때 true가 된다.

만약 아무런 파라메터도 없으면 hello는 나오지 않는다.

~/script$ ./test.sh
Your basename is test.sh
~/script$ ./test.sh a
Your basename is test.sh
Hello a

test는 다음과 같이 활용 될 수 있다.

  • test expression : expression이 참이냐 false냐
  • test ! expression : 결과 반대
  • test expression -a expression : and
  • test expression -o expression : or
  • test -n string : string에 값이 있는가?
  • test -z string : string에 값이 없는가?
  • test string = string : 두 string은 같은가?
  • test 3 -gt 0 : 3은 0보다 큰가?
  • test 3 -lt 0 : 3은 0보다 작은가?
  • test 3 -eq 0 : 3은 0과 같은가?
  • test 3 -nq 0 : 3은 0과 같지 않은가
String을 shell script내에서 test condition으로 사용할 경우 반듯이 ""를 해야한다
if [ "mother" = "Mother" ]
~/script$ test $USER = root
~/script$ echo $?
1

~/script$ test ! $USER = root
~/script$ echo $?
0

~/script$ test -n iam
~/script$ echo $?
0

~/script$ test -z iam
~/script$ echo $?
1

~/script$ test 3 -gt 0
~/script$ echo $?
0

~/script$ test 3 -lt 0
~/script$ echo $?
1

~/script$ test 3 -eq 0
~/script$ echo $?
1
  • test -d 디렉토리 : 디렉토리 인가?
  • test -x 실행파일 : 실행가능한가?
  • test -f 파일 : 파일이 맞는가?
  • test -r 파일 : 읽을 수 있는가?
$ test -d script/
~$ echo $?
0
~$ test -x script/
~$ echo $?
0
~$ test -f script/
~$ echo $?
1

복합 컨디션

  • test a = b || test b = b : a와b의 string이 같거나 b와 b의 스트링이 같으면  true이다
~/script$ test a = b || test b = c
~/script$ echo $?
1
~/script$ test a = b || test b = b
~/script$ echo $?
0

 

 

if condition

#!/bin/bash
if [ $# -lt 1 ];then
    echo "no param"
fi
echo "all params : $*"
exit 0

입력된 파라메터가 1보다 작은지 확인하는 코드

if 컨디션 ; then

if

으로 구성된다

~/script$ ./if.sh a b c
all params : a b c
~/script$ ./if.sh
no param
all params :

else를 추가할 수 있다

if 컨디션 ; then

else

if

#!/bin/bash
if [ $# -lt 1 ];then
    echo "no param"
else
    echo "all params : $*"
fi
exit 0
~/script$ ./if.sh
no param
~/script$ ./if.sh abc
all params : abc

복합 컨디션은 아래와 같다

#!/bin/bash
if [ $# -lt 1 ] || [ -n "abc" ];then
    echo "no param"
else
    echo "all params : $*"
fi
exit 0

위의 경우는 무조건 no param이 나오게 된다

-n "abc"는 항상 참이기 때문이다.

~/script$ vi if.sh 
~/script$ ./if.sh a b c d
no param

 

 

Script example

Ping

#!/bin/bash
read -p "server address : " serveraddr
ping -c 3 $serveraddr 2>1 > /dev/null || echo "Server Dead"

ping에서 -c는 retry의 횟수이다. 2>1은 standard 아웃풋을 모두 /dev/null로 보냄으로써 화면에 표시하지 않겠다는 의미이다.

마지막으로 ping의 결과가 0이 아닌 결과로 error로 판단되면 Server Dead가 표시된다.

~/script$ ./ping.sh 
server address : 1.2.3.4
Server Dead

 

파일 내용 읽기

#!/bin/bash
while read line
do
    echo $line
done
/script$ ./readfile.sh < ping.sh 
#!/bin/bash
read -p "server address : " serveraddr
ping -c 3 $serveraddr 2>1 > /dev/null || echo "Server Dead"

 

 

 

 

728x90
반응형