[Bash] Shell Script 기초 개념

pingping·2021년 2월 20일
0

공백

  • 명령의 기본적인 구조를 살펴보면 다음과 같다.

  • 제일 앞에 명령 이름이 오고 다음에 '공백' 그 다음 첫 번째 인수 '공백', ...

    ⇒ 명령은 공백으로 분리하여 작성한다.

command arg1 arg2 arg3

예시

  • [ 를 명령으로 인식하고 ]를 마지막 인자로 인식하기 때문에 [ ]는 각각 공백으로 분리하여 작성되어야 한다.

    ⇒ argument를 상기하면 이해하기 쉽다.

test@test:~$ [ a = b ]; echo $?
1

test@test:~$ [ a=b ]; echo $?
0

test@test:~$ { echo 1;echo 2; }
1
2

test@test:~$ { echo 1; echo 2; }
1
2

test@test:~$ {echo 1; echo 2; }
-bash: syntax error near unexpected token `}'

test@test:~$ { echo 1; echo 2;}
1
2

test@test:~$ { echo 1; echo 2; }
1
2

test@test:~$ { echo 1;echo 2;}
1
2
  • 기본적인 명령 구조를 갖지 않는 문장 ⇒ 대입 연산 ( 대입 연산을 하는 문장을 명령을 작성하는 식으로 하면 오류가 발생한다. )
  • Command 'aa' 라고 출력됨을 확인할 수 있다. ⇒ 명령어로 인식해버림
test@test:~$ clear
test@test:~$ aa = 10

Command 'aa' not found, but can be installed with:

sudo apt install astronomical-almanac

test@test:~$ aa=10
test@test:~$ echo $aa
10

True & False

  • If 문에서 참, 거짓을 판단할 때 프로그래밍 언어에서는 0이 거짓이고 그 외 값은 참이지만 Bash shell 에서는 반대이다.

    ⇒ 0 : 프로그램의 정상종료 , 그 외의 숫자 : 오류

  • 종료 상태 값 ( $? ) 으로 확인할 수 있음

$ date -@      # 인수를 잘못 사용하여 오류발생
date: invalid option -- '@'
Try 'date --help' for more information.

$ echo $?      # 0 이 아닌 종료 상태 값은 if 문에서 모두 거짓에 해당합니다.
1

$ date +%Y
2015

$ echo $?      # 정상종료 됐으므로 0 을 리턴. if 문에서는 참이 됩니다.
0

Return

test@test:~$ func() { expr $1+$2; return 5;}
test@test:~$ func 1 2
1+2
test@test:~$ func() { expr $1 + $2; return 5;}
test@test:~$ func 1 2
3
test@test:~$ echo $?
5
test@test:~$ AA=$( func 1 2 )
test@test:~$ echo $AA
3

Escape

  • Shell 작성을 위해 shell에서 제공하는 키워드, 메타문자, glob 문자들이 같이 사용되는 환경이기 때문에 명령문에서 동일한 문자가 왔다면 escape하거나 quote하여 명령문을 위한 스트링으로 만들어 줘야 오류가 발생하지 않는다.
# 명령문에 shell 에서 사용하는 glob 문자 '*' 가 포함되어 에러 발생
test@test:~$ expr 3 * 4
expr: syntax error

test@test:~$ expr 3 \* 4
12

test@test:~$ expr 3 "*" 4
12

test@test:~$ [ 3 \< 4 ]

test@test:~$ echo [ 3 \< 4 ]
[ 3 < 4 ]

test@test:~$ [ 3 < 4 ]
-bash: 4: No such file or directory

# '<' , '>' 문자는 shell 에서 사용되는 redirection 메타문자 
# 마찬가지로 escape 하지 않으면 정상적으로 실행되지 않고 오류가 발생합니다
test@test:~$ [ 5 \< 4 ]

test@test:~$ [ 3 \< 4 ]

test@test:~$ echo $?
0

test@test:~$ [ 3 \< 2 ]

test@test:~$ echo $?
1

test@test:~$ test 3 \> 2

test@test:~$ test 1 \> 2

test@test:~$ echo $?
1

# '( )' ';' 문자도 shell 에서 사용하는 메타문자
$ find * ( -name "*.log" -or -name "*.bak" ) -exec rm -f {} ;
bash: syntax error near unexpected token '('

# 다음과 같이 모두 escape 해줘야 오류없이 정상적으로 실행이 됩니다.
$ find * \( -name "*.log" -or -name "*.bak" \) -exec rm -f {} \;

단어 분리

  • 변수나 명령치환을 quote하지 않으면 값이 출력될때 IFS(Internal Field Separator)에 의해 단어가 분리된다. (기본적으로 공백문자로 구성)
  • 그러므로 뜻하지 않게 인수가 2개 이상 늘어난다거나 공백이 포함된 파일이름이 분리가 되는 오류가 발생할 수 있다.
test@test:~$ AA="hello world"
test@test:~$ echo $AA
hello world

test@test:~$ func () {
> echo arg1 : "$1"
> echo arg2 : "$2"
> }

test@test:~$ func $AA
arg1 : hello
arg2 : world

test@test:~$ func "$AA"
arg1 : hello world
arg2 :

명령의 옵션

  • -n이나 - -bar이 grep 명령의 옵션으로 인식이 되어 정상적으로 명령이 실행되지 않는 경우

    ⇒ 이와 같은 경우 - -를 사용하여 "이 뒤로부터는 옵션이 아니다"라고 선언할 수 있다.

test@test:~$ grep -r "-n"
Usage: grep [OPTION]... PATTERN [FILE]...
Try 'grep --help' for more information.

test@test:~$ grep -r -- "-n"
.profile:if [ -n "$BASH_VERSION" ]; then
.bashrc:if [ -n "$force_color_prompt" ]; then
.bashrc:alias alert='notify-send --urgency=low -i "$([ $? = 0 ] && echo terminal || echo 
error)" "$(history|tail -n1|sed -e '\''s/^\s*[0-9]\+\s*//;s/[;&|]\s*alert$//'\'')"'

test@test:~$ grep -r -- "--bar"

STDIO을 나타내는 '-'

test@test:~$ echo hello world | cat -
hello world

test@test:~$ cat hello.txt
1111
2222
3333

test@test:~$ echo hello world | cat - hello.txt
hello world
1111
2222
3333

test@test:~$ echo hello world | cat hello.txt -
1111
2222
3333
hello world

cd 명령은 종료 상태 값을 확인해야 한다.

  • 아래와 같이 없는 디렉터리에 접근하려다가 실패할 경우 현재 디렉터리에 있는 모든 파일들을 날려버릴 수 있다.
cd ~/tempdir
rm -rf *
  • cd 명령의 성공 여부를 체크해야 한다.
cd ~/tempdir && rm -rf *

cd ~/tempdir || { echo >&2 "cd ~/tempdir failed"; exit 1 ;}
. . .

$ 문자를 이용하는 확장

  1. ${ } : 매개변수 확장
$AA, ${AA}, ${AA: -5}, ..
  1. $(( )) : 산술 확장
$(( 1 + 2 ))
  1. $ ( ) : 명령 치환
$( echo "1.3 + 2.5" | bc )
  • : 명령 대체 기능을 수행하는데, 명령의 결과를 대체해서 사용한다.
  • ' ' : 모든 문자나 특수 문자들을 일반 문자로 취급
  • " " : $, `, \ 를 제외한 모든 문자들을 일반 문자로 취급한다.
  • $ : shell 변수 기호로 뒤에 오는 문자열을 변수로 취급한다.
  • ( ) : 부속 (subshell)을 뜻하는데 하나의 셸 단위로 묶어준다.
  • \ : 바로 다음에 오는 특수 문자의 기능을 없애며 셸에서 긴 명령행 입력 시에 행을 연장할 때도 사용한다.
  • : bracket이라고도 부르며 선택할 수 있는 문자을 나열하여 ~중의 하나 라고 표현된다.
  • ; : shell 명령 분리자, 명령어를 순차적으로 실행할 때 사용
  • { } : 안에 제시된 문자열 중 하나로 대치시킨다.
    • b{ed, olt, ar}s : beds, bolts, bars

기타 변수 대응법

  • ${name}

    : name이라는 변수에 들어있는 값으로 치환한다. 변수명 다음에 다른 문자가 연이어 나올 경우 유용함

  • ${name:=value}

    : name이 null이면 value로 할당하여 저장하고, name에 값이 있으면 그 값을 사용한다. 변수에 기본값을 지정할 때 유용하다.

  • ${#name}

    : name의 문자열 길이를 반환한다.

  • ${name:?value}

    : 기존에 name 값이 있다면 기본값으로 하고, 값이 없으면 error을 내면서 value의 값을 보여준다.


  • $IFS : 입력필드 구분자로서 셸 상에서 입력을 읽어 들일 때 글자를 구분하기 위한 목적으로 사용되며 스페이스, 탭, 개행문자를 사용한다.

  • 아규먼트 변수
    • $0 : 실행된 셸 스크립트 명
    • $1 : 스크립트에 넘겨진 첫 번째 아규먼트
    • $2 : 스크립트에 넘겨진 두 번째 아규먼트
    • $# : 셸 스크립트에 넘겨진 아규먼트 갯수
    • $@ : IFS 환경 변수를 사용하지 않음
    • $* : 스크립트에 전달된 인자 전체를 하나의 변수에 저장하면 IFS 변수의 첫 번째 문자로 구분 ( 통으로 전달될 때 사용 )
    • $? : 실행한 뒤의 return 값을 반환
    • $- : 현재 Shell이 호출될 때 사용한 옵션들

set : 셸 변수를 출력하는 명령어

env : 환경 변수를 출력하는 명령어

export : 특정 변수의 범위를 환경 변수의 데이터 공간으로 전송하여 자식 프로세스에서도 특정 변수를 사용 가능하게 한다. 전역 변수의 개념

unset : 선언된 변수를 제거한다.


\f : 앞 문자열만큼 열을 밀어서 이동

\n : 새로운 줄로 바꾼다

\r : 앞 문자열의 앞부분부터 뒷문자열 만큼 대체하고 반환한다.

\t : 탭 만큼 띄운다.

test

  • 문자열과 일치하는 정규식 부분을 찾아 해당하는 정규식 다음에 있는 명령어를 실행시킨다.

*) 는 해당하지 않은 모든 경우를 처리한다.

$ cat case1.sh
#!/bin/bash

echo "################################################"

echo "1. whoami 2. date 3. pwd 4. ls -l "

read number
case $number in
        1) whoami;;
        2) date;;
        3) pwd;;
        4) ls -l;;
        *) echo "번호를 입력하시오 (1 ~ 4)";;
esac

$ ./case1.sh
################################################
1. whoami 2. date 3. pwd 4. ls -l
1
user

$ ./case1.sh
################################################
1. whoami 2. date 3. pwd 4. ls -l
2
Sat Feb 20 13:59:43 KST 2021

#!/bin/bash
echo "OS ? "

select var in "CentOS7" "Ubuntu" "Windows"
do
        echo "Your OS is $var"
        break
done

$ ./select.sh
OS ?
1) CentOS7
2) Ubuntu
3) Windows
#? 1
Your OS is CentOS7

while

#!/bin/bash
var=1
while [ "$var" -le 5 ]
do
        echo $var
#       var=$[ $var + 1 ]
#       var=$(expr $var + 1)
        var=`expr $var + 1`
done

until

  • while문과 동일한 효과이지만 조건이 반대이다.
    ⇒ while문은 조건이 참일 동안, until은 조건이 거짓일 동안 루프를 수행
$ cat until.sh
#!/bin/bash

COUNTER=20
until [ $COUNTER -lt 10 ]
do
        echo COUNTER is $COUNTER
        COUNTER=`expr $COUNTER - 1`
done

$ ./until.sh
COUNTER is 20
COUNTER is 19
COUNTER is 18
COUNTER is 17
COUNTER is 16
COUNTER is 15
COUNTER is 14
COUNTER is 13
COUNTER is 12
COUNTER is 11
COUNTER is 10

패턴과 패턴 비교

  • 일종의 문자열을 연산하는 것으로 특정한 pattern을 놓고 변수의 문자열 값이 일부분이라도 이 패턴과 일치하는지 검사할 때 쓰인다. 이 패턴에는 와일드카드 문자 ( *, ?, [ ] 를 포함한 문자 세트)를 포함해도 된다.

$ cat pat.sh
#!/bin/bash

# var not exists
unset var1
echo ${var1:-string2}

# var exists
var1=string1
echo ${var1:-string2}

var1=/var/log/apt
# var/log/apt
echo ${var1#*/}
# apt
echo ${var1##*/}

var2=/var/log/apt/log/ifconfig.cfg
# /var/log/apt/
echo ${var2%log*}
# /var/
echo ${var2%%log*}

$ ./pat.sh
string2
string1
var/log/apt
apt
/var/log/apt/
/var/

Exit Code

  • Ref

https://www.youtube.com/channel/UCj-hVnpgUU8zGM4Q7pedDOA

profile
Cloud Infra Engineer & interested in python, IaC, k8s

0개의 댓글