Linux

[Linux] 쉘(shell) 스크립트 기초

SungWookKang 2023. 9. 5. 19:20
반응형

[Linux] (shell) 스크립트 기초

 

l  Linux

 

스크립트(shell script) 셸이나 명령줄 인터프리터(Interpreter)에서 실행되도록 작성되었거나 운영 체제를 위해 쓰인 스크립트이다. 스크립트가 수행하는 일반 기능으로는 파일 이용, 프로그램 실행, 문자열 출력 등이 있다. 스크립트에서는 .sh라는 파일 확장자를 가진 파일이 특정 종류의 스크립트를 가리키는 것이 보통이지만, 대부분의 스크립트는 파일 확장자를 지니지 않는다. 스크립트는 인터프리터 방식이므로 라인별로 읽어 실행함으로 속도가 다소 느리다는 단점이 있다.

스크립트라는 말은 유닉스 쉘을 위해 쓰인 스크립트를 말하는 반면, command.com(도스) cmd.exe (윈도우) 명령 스크립트는 보통 배치 파일이라고 불린다.  최근엔 마이크로소프트가 개발한 CLI쉘인 파워쉘이 있다.

 

이번 포스트에서는 가장 널리 쓰이는 bash 쉘을 사용하는 스크립트를 설명한다.

 

문장의 시작 (#!/bin/bash)

스크립트의 번째줄에는 항상 #!/bin/bash 기입되어 있어야 한다. 내용은 쉘이 실행될 어떤 쉘로 스크립트를 실행할지 정의하는 곳이다. 쉘에는 다양한 버전이 있다. 아래 예시는 hello.sh 라는 스크립트를 생성하고 실행하는 방법을 설명한다. 아래 스크립트를 hello.sh라는 파일을 생성하고 입력한다.

#!/bin/bash
 
echo "Hello, Shell Script"

 

아래 명령을 사용하여 스크립트를 실행하면 Hello, Shell Script 출력한다. 스크립트 명령어에 대한 부분은 아래에서 자세히 설명하도록 한다. 만약 실행이 되지 않는다면 실행 권한이 없어 발생한것으로 chmod 777 hello.sh 실행하여 권한 변경 실행한다.

$sh hello.sh

 

 

 

주석 처리 (#)

스크립트에서 단일 주석은 (#) 사용한다.

#!/bin/bash
 
#여기는 주석입니다.
#주석은 실행되지 않습니다.
 
echo "Hello, Shell Script"

 

블록 주석은 : <<”END” ~ ~ END 사용한다.

#!/bin/bash
 
: << “END” #주석 시작
여기는 주석입니다.
주석은 실행되지 않습니다.
END #주석
 
echo "Hello, Shell Script"
 

 

 

입력(read), 출력(echo)

echo 출력, read 입력을 있다. 위에서 이미 echo 사용하였으므로 read 사용하여 입력한 값을 출력하도록 스크립트를 작성해 본다. 입력 받은 값을 변수로 사용하려면 $기호를 사용한다.

#!/bin/bash
read input_value
echo "Hello, $input_value!"

 

스크립트를 실행하면 입력값이 발생할 때까지 실행되지 않고 대기한다. hahaha라고 입력하면 입력 받은 값이 변수로 사용되어 결과가 출력된다.

$sh hello.sh
hahaha

 

 

 

변수 특징

l  변수의 타입에는 로컬변수와 전역변수, 환경변수, 예약변수, 매개변수 다양하다.

l  변수는 , 소문자를 구별한다.

l  변수명에는 영문자, 숫자, 언더바( _ ) 사용된다.

l  변수의 이름은 숫자를 포함할 있지만, 숫자로 시작할 없다.

l  변수에는 모든 값을 문자열로 저장된다.

l  변수에는 자료형을 기입하지 않기 때문에 어떠한 값도 입력할 있다. (변수에 타입을 지정할 수도 있다.)

l  값을 사용할 때는 변수명 앞에 특수문자 "$" 사용한다. (Ex. echo ${data})

l  값을 대입(삽입) 때는 특수문자 "$" 사용하지 않는다. (Ex. data=mac)

l  변수를 생성할 때는 "=" 대입문자 앞뒤로 공백이 없어야 한다. (Ex. data="abcd") 문자열인 경우 “ “ 감싼다.

l  하나의 변수에 개의 값만 보존된다.

l  변수 값이 덮어 쓰기되는 것을 방지하기 위해서는 readonly 사용한다.

l  변수를 unset으로 삭제할 있다. (readonly 선언된 것은 삭제가 불가능하다.)

 

스크립트에서 시스템이 사용하는 예약어가 있다. 가지 대표적인 예약어이다.

변수 설명
HOME 사용자 디렉토리 의미
PATH 실행 파일의 경로
LANG 프로그램 실행 지원되는 언어
UID 사용자의 UID
SHELL 사용자가 로그인시 실행되는
USER 사용자 계정 이름
FUNCNAME 현재 실행되고 있는 함수 이름
TERM 로그인 터미널
Set 변수를 출력하는 명령어
Env 환경 변수를 출력하는 명령어
Export 특정 변수의 범위를 환경 변수의 데이터 공간으로 전송하여 자식 프로세스에서도 특정 변수를 사용 가능하게 한다.
unset 선언된 변수를 제거

 

 

전역변수, 지역변수

쉘에서 선언된 변수는 기본적으로 전역변수(global variable)이며, 함수안에서 지역변수(local variable) 사용할 있는데, 이때에는 local 붙여서 사용한다. 함수안에서 local 사용하지 않으면 전역 변수의 값을 덮어쓰게 된다.

#!/bin/bash
 
# 전역 변수 지정
string="hello shell, global variable"
 
function string_test() {
    # local 붙여야 지역변수로 인식. 만약 local 빼면 전역변수에 덮어쓰기로 적용
    local string="hello shell, local variable"
    echo ${string}
}
 
# 함수 호출
string_test # > hello shell, local variable
echo ${string} # > hello shell, global variabl
 
# 변수 초기화
unset string

 

 

 

변수 타입 지정

기본적으로 Bash 문자열만 저장하지만 데이터 타입을 지정할 수도 있다.

변수타입 설명
declare -r 읽기 전용 타입
declare -i 정수형 타입
declare -a 배열 타입
declare -A 연관배열(MAP) 타입
declare -f 함수 타입
declare -x 환경변수(export) 지정으로 외부 환경에서도 변수를 사용할 있다.

 

 

읽기전용 변수 (readonly)

변수에 값을 덮어 쓸수 있지만 readonly 지정한 경우 변수를 덮어쓸수 없다. 아래 예시는 readonly에서 변수 덮어쓰기를 시도할 발생한 오류를 확인할 있다.

#!/bin/bash
 
var="변수1"
VaR_2="변수2"
echo "Var_2=$VaR_2"
 
VaR_2="VaR_2 변경합니다."
echo ${VaR_2}
 
readonly var
var="readonly var 변수를 변경해 봅니다."

 

 

 

변수 값의 치환

변수의 값을 치환할 사용하는 문법을 표로 정리하였다.

문법 설명
${var} 변수 값을 바꿔 넣는다.
${var:-word} 변수가 아직 세팅되지 않거나 공백 문자열의 경우 word 반환한다. var에는 저장되지 않는다.
${var:=word} 변수가 아직 세팅되지 않거나 공백 문자열의 word 반환한다. var 저장된다.
${var:?word} 변수가 아직 세팅되지 않거나 공백 문자열의 경우 치환에 실패하고, 스탠다드 에러에 에러가 표시된다.
${var:+word} 변수가 세팅되지 않은 경우 word 반환된다. var에는 저장되지 않는다.

 

#!/bin/sh
 
echo "1 - ${var:-wordSetInEcho1}"
echo "2 - var = ${var}"
echo "3 - ${var:=wordSetInEcho3}"
echo "4 - var = ${var}"
unset var
echo "5 - ${var:+wordSetInEcho5}"
echo "6 - var = $var"
var="newVarValue"
echo "7 - ${var:+wordSetInEcho7}"
echo "8 - var = $var"
echo "9 - ${var:?StandardErrorMessage}"
echo "10 - var = ${var}"

 

 

문자열 패턴 비교는 아래 조건을 사용할 있다.

문법 설명
${var%pattern} 끝에서 부터 word 패턴이 일치하는 var 최소 부분(첫번째 일치) 제거하고 나머지를 반환
${var%%pattern} 끝에서 부터 word 패턴이 일치하는 var 최대 부분(마지막 일치) 제거하고 나머지를 반환
${var#pattern} 처음 부터 word 패턴이 일치하는 var 최소 부분( 번째 일치) 제거하고 나머지 부분을 반환
${var##pattern} 처음 부터 word 패턴이 일치하는 var 최대 부분(마지막 일치) 제거하고 나머지 부분을 반환

 

 

환경 변수 (export)

스크립트에서 변수명 앞에 export 붙여 환경변수(environment variable) 사용할 있다. 다만 환경변수 사용시 시스템에 미리 정의된 예약변수와 겹치지 않게 주의한다. 실습을 위해 개의 파일을 준비한다.

shell1.sh
#!/bin/bash
 
echo ${export_variable}

 

shell2.sh
#!/bin/bash
 
#환경변수 선언
export export_variable="export test, hello shell"
 
#변수값을 받아서 사용할 스크립트 호출
/파일 경로/shell1.sh

 

shell2.sh 실행한다. Export 변수의 값은 shell1.sh 호출할 사용된다.

 

 

매개변수

프로그램에서 실행할 인자를 사용하듯 스크립트에서도 매개변수를 사용할 있다. 규칙이 있는데, 실행한 스크립트 이름은 ${0}, 이후 인자 값들은 ${1}. ${2}…으로 사용한다.

변수 기능
$0 스크립트명
$1 ~ $9 인수 1 번째부터 인수 9번째까지 사용
$# 스크립트에 전달될 인수의
$* 모든 인수를 모아 하나로 처리
$@ 모든 인수를 각각 처리
$? 직전에 실행한 커멘드의 종료 (0 성공, 1 실패)
$$ 스크립트의 프로세스ID
$! 마지막으로 실행한 백그라운드 프로세스 ID

 

아래 스크립트는 매개변수를 입력 받아 어떻게 사용되는지 알아보는 예시이다.

#!/bin/sh
 
echo "\$0script name: $0"
echo "\$11st param: $1"
echo "\$22nd param: $2"
echo "\$#number of param: $#"
echo "\"\$*\": \"$*\""
echo "\"\$@\": \"$@\""
VAR="exit code is  0 "
echo $?

 

실행 결과는 아래와 같이 나타난다.

 

 

배열 (Array)

스크립트에서 배열은 1차원 배열만 지원하며 중괄호를 사용해야 한다. 배열 원소는 소괄호안에 공백으로 구분하며, 배열 원소는 문자열이든 숫자형이던 상관없이 있다.

#!/bin/bash
 
arr=("hello" "shell" 1 2 3 4 5)
 
echo "배열 전체 : ${arr[@]}"
echo "배열 원소의 갯수 : ${#arr[@]}"
echo "배열 첫번째 : ${arr}, 혹은 ${arr[0]}"
echo "4 index 갖는 배열의 원소 : ${arr[4]}"
 
arr[5]="four"
 
echo "4번째 index 갖는 배열의 원소 : ${arr[4]}"
 
# 4번째 해제
unset arr[4]
echo "4 배열 삭제 완료"
echo "4 index 갖는 배열의 원소 : ${arr[4]}"
echo "5 index 갖는 배열의 원소 : ${arr[5]}"

 

 

 

함수(function)

스크립트에서도 함수를 사용할 있다. 함수의 정의 형식은 아래와 같다. 주의할 점은 함수가 호출되기 전에 함수가 정의되어 있어야 하며 호출할 때는 괄호를 사용하지 않고 호출한다.

#!/bin/bash
 
function func(){
        echo "func()"
}
 
#함수 호출
func

 

 

비교문(if..fi)

쉘에서는 비교문이 약간 특이하다. bash if문의 특이한 점은 fi 대괄호([ ]) 이다. 다른 언어와 달리 중괄호를 사용하지 않기 때문에 fi if문의 끝을 알려주어야 한다. 주의해야 점은 if 뒤에 나오는 대괄호 [ ] 조건식 사이에는 반드시 공백이 존재해야 한다.

 

비교문에는 다양한 연산자를 사용할 있다.

연산자 설명
1 -eq 2 # 값이 같음(equal)
1 -ne 2 # 값이 같지 않음(not equal)
1 -lt 2 # 1 2보다 작음(less than)
1 -le 2 # 1 2보다 작거나 같음(less or equal)
1 -gt 2 # 1 2보다 (greater than)
1 -ge 2 # 1 2보다 크거나 같음(greater or equal)

 

문자열 비교

연산자 설명
문자1 = 문자2 # 문자1 문자2 일치 (sql같이 = 하나만 써도 일치로 인식)
문자1 == 문자2 # 문자1 문자2 일치
문자1 != 문자2 # 문자1 문자2 일치하지 않음
-z 문자 # 문자가 null 이면
-n 문자 # 문자가 null 아니면
문자 == 패턴 # 문자열이 패턴과 일치
문자 != 패턴 # 문자열이 패턴과 일치하지 않음

 

논리 연산자

연산자 설명
조건1 -a 조건2 # AND
조건1 -o 조건2 # OR
조건1 && 조건2 # 양쪽 성립
조건1 || 조건2 # 한쪽 또는 양쪽다 성립
!조건 # 조건이 성립하지 않음
true # 조건이 언제나 성립
false # 조건이 언제나 성립하지 않음

 

산술연산자 (쉘에서는 expr 숫자 연산자 숫자형식으로 사용한다.)

연산자 의미 예시
+ 덧셈 echo `expr 10 + 20` => 30
- 뺄셈 echo `expr 20 - 10` => 10
\* 제곱 echo `expr 11 \* 11` => 121
/ 나눗셈 echo `expr 10 / 2` => 5
% 나머지 echo `expr 10 % 4` => 2
= 저장 a=$b b 값은 a 저장
== 동일 [ "$a" == "$b" ] $a $b 동일하는 경우 TRUE 반환
!= 다름 [ "$a" != "$b" ] $a $b 동일하지 않는 경우 TRUE 반환

 

 

예제로 살펴본다.

#!/bin/bash
 
function func(){
        a=10
        b=5
 
        if [ ${a} -eq ${b} ]; then
                echo "a b 같다."
        fi
 
        if [ ${a} -ne ${b} ]; then
                echo "a b 같지 않다."
        fi
 
        if [ ${a} -gt ${b} ]; then
                echo "a b보다 크다."
        fi
 
        if [ ${a} -ge ${b} ]; then
                echo "a b보다 크거나 같다."
        fi
 
        if [ ${a} -lt ${b} ]; then
                echo "a b보다 작다."
        fi
 
        if [ ${a} -le ${b} ]; then
                echo "a b보다 작거나 같다."
        fi
 
}
 
#함수 호출
func

 

 

 

여러 조건을 사용할때, 다른 언어에서는 else if역할을 하는 것을 스크립트에서는 elif라는 것을 사용하여 else if 동일한 역할을 있다. elif[ 조건 ]; then 형식으로 사용한다. 만약 반복문에서 사용하여 조건이 참일때 반복문을 멈추고 싶을때 break라는 키워드를 사용하여 반복문을 멈출 있다.

#!/bin/bash
 
case ${1} in
        "linux") echo "리눅스" ;;
        "unix") echo "유닉스" ;;
        "windows") echo "윈도우즈" ;;
        "MacOS") echo "OS" ;;
        *) echo "해당없음" ;;
esac

 

 

파일 관련 조건문

스크립트는 명령어를 다루는 스크립트이기 때문에 파일이 존재하는지, 디렉토리가 존재하는지 파일과 관련된 조건 여부도 조건문을 통해 있다. 아래 표는 조건을 정리한 것이다. , 거짓을 판단할 , 느낌표(!) 사용하면 반대로 결과를 출력한다.

조건 설명
if [ -d ${변수} ]; then ${변수} 디렉토리가 존재하면 true.
if [ ! -d ${변수} ]; then ${변수} 디렉토리가 존재하지 않으면 true.
if [ -e ${변수} ]; then ${변수}라는 파일이 존재하면 true.
if [ ! -e ${변수} ]; then ${변수}라는 파일이 존재하지 않으면 true.
if [ -L ${변수} ]; then 파일이 symbolic link이면 true.
if [ -s ${변수} ]; then 파일의 크기가 0보다 크면 true.
if [ -S ${변수} ]; then 파일 타입이 소켓이면 true.
if [ -r ${변수} ]; then 파일을 읽을 있으면 true.
if [ -w ${변수} ]; then 파일을 있으면 true.
if [ -x ${변수} ]; then 파일을 실행할 있으면 true.
if [ -f ${변수} ]; then 파일이 정규 파일이면 true.
if [ -c ${변수} ]; then 파일이 문자 장치이면 true.
if [ ${변수1} -nt ${변수2}]; then 변수1 파일이 변수2 파일보다 최신 파일이면 true.
if [ ${변수1} -ot ${변수2}]; then 변수1 파일이 변수2 파일보다 최신이 아니면 true.
if [ ${변수1} -ef ${변수2}]; then 변수1 파일과 변수2 파일이 동일하면 true.

 

 

CASE

일반적인 CASE 사용법과 비슷하지만 CASE 끝에 세미콜론을 2개씩 사용하는 특징이 있다. 그리고 CASE 끝은 ESAC 닫아준다.

#!/bin/bash
 
DRINK="coffee"
case "$DRINK" in
    "beer") echo "맥주입니다"
    ;;
    "juice") echo "주스입니다"
    ;;
    "coffee") echo "개발자에겐 카페인 필수!"
    ;;
esac

 

 

CASE에서 OR 연산도 가능하다.

COUNTRY=korea
 
case $COUNTRY in
  "korea"|"japan"|"china") # or 연산도 가능하다
    echo "$COUNTRY is Asia"
    ;;
  "USA"|"Canada"|"Mexico")
    echo "$COUNTRY is Ameria"
    ;;
  * )
    echo "I don't know where is $COUNTRY"
    ;;
esac

 

 

반복문(for)

반복문을 빠져나갈때에는 break 사용하고, 현재 반복분이나 조건을 건너뛸때에는 continue 사용한다.

#!/bin/bash
 
# 초기값; 조건값; 증가값을 사용한 정통적인 for
for ((i=1; i<=4; i++)); do
    echo $i
done

 

 

 

반복문(for in)

#!/bin/bash
 
function func(){
        echo "사용예1"
        for i in 1 2 3 4 5
        do
                echo "${i}"
        done
 
        echo "사용예2"
        list="1 2 3 4 5"
        for i in ${list}
        do
                echo "${i}"
        done
 
        echo "사용예3"
        for i in {1..5}
        do
                echo "${i}"
        done
 
        echo "사용예4: 크기를 2만큼 증가시키면서 출력"
        for i in {1..5..2}
        do
                echo "${i}"
        done
 
        echo "사용예5: 배열을 이용"
        arr=(1 2 3 4 5)
        for i in "${arr[@]}"
        do
                echo "${i}"
        done
 
 
        echo "사용예6: C 유사한 형식의 for"
        for ((i=0; i<5; i++)); do
                echo "${i}"
        done
}
 
#함수 호출
func

 

 

 

반복문 (while)

수행 조건이 true일때 실행되는 반복문이다.

count=0
while [ ${count} -le 5 ];
do
    echo ${count}
    count=$(( ${count}+1 ))
done

 

 

이중 괄호를 사용하면 논리 기호로도 사용 가능하다.

count=0
while (( ${count} <= 5 ));  # 이중괄호 사용하면 논리기호 사용 가능
do
    echo ${count}
    count=$(( ${count}+1 ))
done

 

 

반복문(until)

수행 조건이 false 일때 실행되는 반복문이다. 조건이 While 반대라고 생각하면 된다.

count2=10
 
until [ ${count2} -le 5 ]; do
    echo ${count2}
    count2=$(( ${count2}-1 ))
done

 

 

 

종료 상태 코드

명령어가 실패할 경우 리턴되는 결과에 따라 다른 동작을 하려면 명령어의 종료 코드를 반환 받아 사용하면 된다. 명령어의 종료 코드는 $? 사용하여 가장 최근 실행한 명령어의 종료 코드를 받을 있다. 정상적인 종료는 0 반환되며 0 아닌 값은 오류라고 판단할 있다. 아래 표는 종료 코드에 따른 설명을 정리한 것이다.

종료 코드 설명
0 성공 (Success)
1 일반적 오류 (Catchall for general errors)
2 내장 명령의 틀린 사용 (Misuse of shell builtins)
126 파일이 실행 가능하지 않음 (Command invoked cannot execute)
127 명령어를 찾을 없음 (Command not found)
128 종료할 잘못된 인수 적용 (Invalid argument to exit)
128+n 치명적인 시그널 n 에러 (Fatal error signal “n”)
130 [Ctrl + C] 조합에 의한 종료 (Script terminated by Control-C)

 

 

이스케이프 문자

명령어 설명
\f 문자열만큼 열을 밀어서 이동한다.
\n 새로운 줄로 바꾼다.
\r 문자열의 앞부분 부터 뒷문자열 만큼 대체하고 반환한다.
\t 만큼 띄운다.

 

 

외에도 다양한 명령이 있다. 명령을 활용하면 리눅스 환경에서 시스템 관련 다양한 정보들을 스크립트로 얻을 있다.

 

 

 

[참고자료]

l   https://ko.wikipedia.org/wiki/%EC%85%B8_%EC%8A%A4%ED%81%AC%EB%A6%BD%ED%8A%B8

l   https://reakwon.tistory.com/136

l   https://inpa.tistory.com/entry/LINUX-%EC%89%98-%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%B0%8D-%ED%95%B5%EC%8B%AC-%EB%AC%B8%EB%B2%95-%EC%B4%9D%EC%A0%95%EB%A6%AC

l   https://engineer-mole.tistory.com/200

 

 

 

2023-09-05 / Sungwook Kang / https://sungwookkang.com

 

 

리눅스, Linux, 쉘스크립트, 쉘사용법, 쉘명령어, shell command, shell script

반응형