[Linux] 리눅스 기본 활용 - shell script 실습

Hyunjun Kim·2025년 3월 23일
0

Data_Engineering

목록 보기
8/153

shell script 실습

1. 변수

  • 영문, 숫자, _만 사용가능하다
  • Unix shell 의 변수 명은 대문자로 작성하는 것이 convention 이다
  • 변수명=변수값 으로 변수 선언 가능 (사이 띄어쓰기 없이 붙여줘야 선언됨)

1.1 변수 사용법

  • $변수명 : 변수 사용

  • readonly 변수명 : 읽기전용 변수로 선언 (읽기 전용 변수 값을 바꾸려고 시도하면 에러 발생)

  • unset 변수명 : 변수 할당 해제

  • Script 내에서 선언한 변수 뿐만 아니라, 환경 변수, 쉘 변수(접속한 shell에서 export로 선언한 변수)도 $변수 로 읽어올 수 있다.


2. 특수목적 변수

내가 변수로 할당하지 않았는데 특수한 문자들로 쓸 수 있는 변수들을 의미한다.

  • $ : 현재 Shell 의 프로세스 아이디
  • $0 : 현재 script 의 파일 이름(0번째 인자, 자기 자신)
  • $n : script 실행시 넘겨준 n번째 인자
  • $# : script 에 넣어준 인자의 개수
  • $* : 모든 인자를 ""로 감싸서 반환
  • $@ : 각 인자를 "" 로 감싸서 반환
  • $? : 마지막으로 실행된 명령어의 종료 상태
  • $! : 마지막 백그라운드 명령어의 프로세스 아이디

2.1. 특수목적 변수 - $* vs $@ 차이 실습

$@$* 의 차이를 구분해서 사용해야한다. 이 둘의 차이는 스크립트가 또 다른 명령어를 불러올 때 드러난다.

arg-callee.sh, arg-caller.sh 예제로 인자가 어떻게 전달되는지 살펴보자.


arg-callee.sh

#!/bin/sh
echo "arg 1: $1"
echo "arg 2: $2"
echo "arg 3: $3"
echo

arg-caller.sh

#!/bin/sh
echo '$* without quotes:'
./arg-callee.sh $*

echo '$@ without quotes:'
./arg-callee.sh $@


echo '$* with quotes:'
./arg-callee.sh "$*"


echo '$@ with quotes:'
./arg-callee.sh "$@"

arg-caller.sh에 실행 권한을 부여하고
arg-caller.sh에 apple banana "cherry pie" 전달

chmod +x arg-caller.sh arg-callee.sh  # 실행 권한 부여
./arg-caller.sh apple banana "cherry pie"

$* 실행 결과:
arg 1: apple
arg 2: banana
arg 3: cherry

  • "cherry pie"가 "cherry"와 "pie"로 나뉘어 전달됨 → 공백이 분리됨
  • $*은 전달된 인자들을 "하나의 문자열로 합쳐서" 반환
  • 개별적인 인자로 전달되지 않고, 공백을 기준으로 나뉨

$@ 실행 결과:
arg 1: apple
arg 2: banana
arg 3: cherry

  • $*과 동일하게 "cherry pie"가 "cherry"와 "pie"로 나뉘어 전달됨
  • $@는 전달된 인자들을 각각의 인자로 유지
  • 그러나, 따옴표 없이 쓰면 $*와 동일한 동작을 하게 됨

"$*" 실행 결과:
arg 1: apple banana cherry pie
arg 2:
arg 3:

  • 모든 인자가 하나의 문자열로 합쳐져 arg 1으로 전달됨
  • $*따옴표로 감싸면 모든 인자가 하나의 문자열로 합쳐져서 전달됨
  • 즉, "apple banana cherry pie"라는 하나의 인자로 전달됨

"$@" 실행 결과:
arg 1: apple
arg 2: banana
arg 3: cherry pie

  • 개별 인자가 그대로 전달됨, "cherry pie"도 하나의 인자로 유지됨
  • $@따옴표로 감싸면 각 인자가 원래 형태 그대로 유지
  • 즉, "apple" "banana" "cherry pie" 세 개의 인자로 전달됨




2.2. 특수목적 변수 -$? (Exit Status) 실습

$? : 마지막으로 실행된 명령어의 상태를 확인

exit 0 : 정상종료, 성공
0이 아닌 다른 숫자값 : 기본적으로 실패를 나타냄


fail.sh

#!/bin/sh
echo "fail"
exit 1
  • "fail"을 출력한 후, 종료 코드 1을 반환
  • exit 1은 비정상 종료 (failure)를 의미

success.sh

#!/bin/sh
echo "success"
exit 0
  • "success"를 출력한 후, 종료 코드 0을 반환
  • exit 0은 정상 종료 (success)를 의미

process-call.sh

#!/bin/sh

./fail.sh  # 또는 ./success.sh

if [ $? -ne 0 ]; then
    echo "failed from sub command"
    exit 1
fi

echo "next step"

process-call.sh 실행 결과

  1. process-call.sh 안에서 적어놓은 ./fail.sh 또는 ./success.sh 실행한다
  2. $?를 확인하여 실행 결과를 판단
    • 0이면 정상 실행
    • 0이 아니면 오류 발생
  3. 오류가 발생하면 "failed from sub command"를 출력하고 exit 1로 종료
  4. 오류가 발생하지 않으면 "next step"을 출력하고 계속 진행




2.3. 특수목적 변수 - $! 실습

$! : 마지막으로 실행된 백그라운드 프로세스의 PID(Process ID)를 반환


process-call.sh

#!/bin/sh

./success.sh

if [ $? -ne 0 ]; then
        echo "failed from sub command"
        exit 1
fi

sleep 100 & # 백그라운드에서 실행
echo "background sleep pid: $!"

echo "next step"

./process-call.sh 실행 결과

출력

success
background sleep pid: 13927
next step
  • success: ./success.sh 실행 결과
  • background sleep pid: 13927: sleep 100 &가 백그라운드에서 실행됨
  • next step: 스크립트 정상 종료

백그라운드 프로세스 확인

ps -ef | grep 13927

출력

  501 13927     1   0  2:57PM ttys000    0:00.00 sleep 100
  501 13966 10215   0  2:58PM ttys000    0:00.00 grep --color=auto --exclude-dir=.bzr --exclude-dir=CVS --exclude-dir=.git --exclude-dir=.hg --exclude-dir=.svn --exclude-dir=.idea --exclude-dir=.tox --exclude-dir=.venv --exclude-dir=venv 13927

백그라운드 프로세스 종료

kill -9 13927

$!은 마지막으로 실행된 백그라운드 프로세스의 PID를 저장하는데
sleep 100 & 을 실행 후 $!기능으로 해당 프로세스의 PID를 확인해 보았다.





3. 조건문 - if

If는 expression 을 이용해서 자유롭게 조건을 넣는다.

  • if는 항상fi로 닫는다.
  • 조건 뒤에는 ; then 으로 다음 수행을 알려준다.
  • elif, else 는 선택적으로 사용한다.
If [ condition ]; then
	${if script}
elif [ condition ]; then
	${elif script}
else
	${else script}
fi




4. 조건문 - case

4.1. case는 미리 정해진 pattern의 선택지를 사용한다.

  • case 는 항상 esac 로 닫는다.
  • case 대상 변수 뒤에는 inpattern 이 와야한다.
  • 위에서부터 차례대로 확인한다.
  • *)는 default, 위의 어떤 조건도 아닐 때 수행한다.
case word in 
pattern 1)
Statement(s) to be executed if pattern1 matches
;;
pattern 2)
Statement(s) to be executed if pattern2 matches
;;
*)
Default condition to be executed
;;
esac

4.1. case문 예제


case.sh

#!/bin/sh

OPTION="${1}" # 첫 번째 입력 인수를 OPTION 변수에 저장

case ${OPTION} in
    -f) FILE="${2}"
        echo "FILE Name is $FILE"
        ;;
    -d) DIR="${2}"
        echo "Dir name is $DIR"
        ;;
    [0-9]) NUM="${1}"
        echo "number is $NUM"
        ;;
    *)
        echo "`basename ${0}`: usage: [-f file] | [-d directory]"
        exit 1
        ;;
esac

4.2. 결과 분석

위에서 case로 만든 패턴, 파일 옵션(-f)을 사용

./case.sh -f test.txt

출력

FILE Name is test.txt
  • -f 옵션을 입력했으므로 FILE="${2}" 실행
  • "FILE Name is test.txt" 출력

패턴 형식의 Regular Excpression도 사용 가능하다.





5. 기본 연산자

Bourne shell(기본연산자) 에는 arithmetic operations(더하기/빼기같은 대수 연산 operator) 이 없기 때문에 외부 프로그램인 awk 또는 expr 를 사용해야 한다.

  • 띄어쓰기 필수! (expr 2+2 로 쓰면 "2+2" 문자열이 그대로 출력됨)
  • 정수 연산만 지원 (소수점 계산 필요하면 bc, awk 사용)
  • 곱하기 * 연산은 앞에 \ 입력 해줘야 함

곱하기 예제

expr 4 \* 2

expr 4 * 2 로 입력했을 경우 출력 : expr: syntax error


5.1. 산술 연산자

  • + : 더하기 expr $a + $b 로 사용한다
  • - : 빼기 expr $a - $b로 사용한다
  • * : 곱하기 expr $a \* $b로 사용한다
  • / : 나누기 expr $a / $b로 사용한다
  • % : 나머지 연산자 expr $a % $b로 사용한다
  • = : 값 할당 a=$b 로 사용하면 b의 값이 a에 할당된다
  • [ $a=$b ] : 값이 같은 경우에 true, 값 비교
  • [ $a!=$b ] : 값이 다른경우에 true (이때 반드시 [ ] 대괄호와 값 사이에 space 가 있어야함.)

5.2. 관계 연산자

  • -eq : 두 값이 같은 경우 true
  • -ne : 두 값이 다른 경우 true
  • -gt : 왼쪽 값이 오른쪽 값보다 큰 경우 true 반환
  • -lt : 왼쪽 값이 오른쪽 값보다 작은 경우 true 반환
  • -ge : 왼쪽 값이 오른쪽 값보다 크거나 같은 경우 true 반환
  • -le : 왼쪽 값이 오른쪽 값보다 작거나 같은 경우 true 반환

실습

relation.sh

#!/bin/sh
if [ $1 -eq $2 ]; then
    echo "$1 is equal to $2"
fi

if [ $1 -ne $2 ]; then
    echo "$1 is not equal to $2"
fi

if [ $1 -gt $2 ]; then
    echo "$1 is greater than $2"
fi

if [ $1 -lt $2 ]; then
    echo "$1 is less than $2"
fi

if [ $1 -ge $2 ]; then
    echo "$1 is greater than or equal to $2"
fi

if [ $1 -le $2 ]; then
    echo "$1 is less than or equal to $2"
fi

./relation.sh 3 5

결과
3 is not equal to 5
3 is less than 5
3 is less than or equal to 5


./relation.sh 4 4

결과
4 is equal to 4
4 is greater than or equal to 4
4 is less than or equal to 4


./relation.sh 6 2

결과
6 is not equal to 2
6 is greater than 2
6 is greater than or equal to 2





5.3. 부울 연산자

! : 논리적인 부정
-o : OR 연산자
-a : AND 연산자


실습 스크립트

bool.sh

#!/bin/sh
if [ !false ]; then
    echo "not false is true"
fi

if [ $1 -gt $2 -o $1 -eq $2 ]; then
    echo "$1 is greter than or equal to $2"
fi

if [ $1 -gt $2 -a $1 -lt $2 ]; then
    echo "never happend"
fi

./bool.sh 1 1

실행결과
not false is true
1 is greater than or equal to 1


./bool.sh 1 2

실행결과
not false is true

어느 경우에도 마지막 세번째 조건에는 해당이 안되는 것을 볼 수 있다.




6. 반복문

while / do / done 을 이용해 반복문을 선언할 수 있다.


while.sh (반복문 예제)

#!/bin/sh
a=0
while [ $a -lt 10 ]
do
    echo "$a"
    a=$(expr $a + 1)
done



7. Quoting Mechanism

7.1. Metacharacters

  • ?[]`"\$;()|^<>는 metacharacters 로 shell 에서 특수한 목적으로 사용된다.
  • ''을 이용하면 위의 특수 목적 문자를 출력할 수 있다.
  • 내부적으로 ''를 사용해야 하는경우 "" 를 이용하여 ''을 출력할 수 있다.
  • ``를 이용하여 ``내부의 shell 명령어를 실행할 수 있다.

연습 예제

echo <-$1500.**>; (update?)[y|n] 을 표현하는 방법으로 아래 두가지를 사용할 수 있다.

  • echo \<-\$1500.\*\*>\; \(update\?\) \[y\|n\]
  • echo '<-$1500.**>; (update?) [y|n]'

test1

스크립트
echo '<-$1500.**>; (update?) [y|n]'

결과
<-$1500.**>; (update?) [y|n]

test2

스크립트
echo "<-$1500.**>; (update?) [y|n]"

결과
<-.**>; (update?) [y|n]

test3

스크립트
echo "<-$1500.**>; (update?!) [y|n]"

결과
zsh: event not found: )

test4

스크립트
echo "<-\$1500.**>; (update?\!) [y|n]"

결과
<-$1500.**>; (update?!) [y|n]

test5

스크립트
echo "`expr 2 + 2` is 2 + 2"

결과
4 is 2 + 2




8. Shell Substitution

8.1. echo command 와 함께 사용되는 escape sequence

  • echo -e : backslash escape 을 적용할 수 있다
  • \\ : backslash
  • \a : alert
  • \n : 줄 바꿈
  • \b : backspace
  • \r : 캐리지 리턴 (맨 앞으로 이동)
  • \c : 줄바꿈 삭제
  • \t : 가로 방향으로 tab 만큼 띄움
  • \f : form 양식
  • \v : 세로 방향으로 tab 만큼 띄움

form 양식 실습

form 양식 은 한 칸을 띄우면서 마지막 줄로 들여쓰기를 한 다음에 입력

스크립트
echo -e "first\fsecond\fthird"

결과
first
     second
           third

실습 2

\v 도 form 양식과 같이 개행하면서 탭을 띄움

스크립트
echo -e "first\vsecond\vthird"

결과
first
     second
           third

캐리지리턴 : 백슬래시 R 만나면 맨 앞으로 간 다음에 거기서부터 내가 입력한 걸로 바꿔줘. 라는 것.

스크립트
echo -e "This is my friend\rwhere"

결과
whereis my friend




9. function

우리가 자주 사용하는 것들을 기능으로 만들고 그 다음부터는 그 기능을 함수 이름으로 호출해서 반복 재사용하고싶을 때 사용.
shell 스크립트같은 경우 위에서 아래로 쭉 읽기 때문에 함수가 위에서 정의가 되어 있어야 밑에서 사용 할 수 있다.

  • function_name (){} : 함수 선언
  • function_name : 함수 사용
  • function_name param1 param2 : 함수에 파라미터 param1 param2 전달

함수 정의 및 사용 예시

Hello (){
	echo "Hello World $1"
	return 10
}
Hello FastCampus

실습 : function.sh

#!/bin/sh
myfunc(){
    echo $1
    echo $2
    echo `expr $1 + $2`
}

myfunc 2 4

echo "$1 $2 are shell param"

./function.sh 1 3

출력

2
4
6
1 3 are shell param

스크립트에 넘겨준 파라미터 1,3 은 똑같이 $1, $2 라고 되어 있지만
함수 안에 것과 shell 스크립트 자체에 있는 것은 다른 것을 확인할 수 있다.

profile
Data Analytics Engineer 가 되

0개의 댓글