더 많은 정보를 담은 리눅스 교재를 배포했습니다. 아래의 페이지에서 리눅스 교재를 받아가세요.
https://reakwon.tistory.com/233
쉘스크립트 기본
쉘 스크립트는 쉘에게 무슨 명령들을 실행할지 알려주는 스크립트 파일입니다. 여기서는 가장 널리쓰이는 bash 쉘을 사용하는 스크립트를 설명하도록 하겠습니다.
#!/bin/bash
스크립트 최상단에는 항상 이 구문이 적혀있어야합니다. 간단하게 hello,world라는 문자열을 출력하는 스크립트를 만들어봅시다. 파일명은 exam.sh로 정해놓고요.
#!/bin/bash
echo "hello, world"
printf "hello, world"
./exam.sh로 스크립트를 실행하면 되는데 혹시 실행이 되지 않는다면 실행 권한이 없어서 그런것이니까 chmod 777 exam.sh로 권한을 바꾸어주면 됩니다. 그 후 결과를 보면 echo는 개행이 되지만 printf는 개행이 되지 않는 다는 것을 알 수 있습니다. 또 C언어 처럼 문자열을 전달할 수도 있는데 포맷 문자 %s를 이용하면 됩니다.
printf "%s, %s" hello, world
1) 변수(Variable)
쉘스크립트에서도 변수를 사용할 수 있는데요. 변수명=값 으로 변수를 선언하고, 사용할때는 앞에 $를 붙여줍니다. 중괄호를 이용하는 방법도 있습니다. ${변수}식으로 중괄호로 묶어서 사용합니다.
#!/bin/bash
h="hello"
w="world"
echo "${h}, ${w}"
여기서 export를 사용하여 다른 쉘 스크립트에서 사용할 수 있게 합니다. exam.sh는 변수 선언하고 export를, exam2.sh는 그 변수를 출력하는 쉘 스크립트입니다.
#!/bin/bash
#exam.sh
export MY_VAR="reakwon"
./exam2.sh
#!/bin/bash
#exam2.sh
echo ${MY_VAR}
매개변수
프로그램에서도 실행할때 인자를 주듯 쉘 스크립트도 역시 그렇게 할 수 있습니다. 실행한 스크립트 이름은 ${0}, 그 이후는 전달받은 인자 값들입니다(${1}, ${2}, ...). 아래의 예제코드를 보면서 이해하실 수 있습니다.
#!/bin/bash
echo "script name:${0}"
echo "매개변수 갯수 :${#}"
echo "전체 매개변수 값 : ${*}"
echo "전체 매개변수 값2 : ${@}"
echo "매개변수 1 : ${1}"
echo "매개변수 2 : ${2}"
예약변수
쉘 스크립트에서 사용자가 정해서 만들 수 없는 이미 정의된 변수가 존재합니다. 그것을 예약 변수라고 하는데요. 몇가지 예약 변수를 보도록 합시다.
변수 | 설명 |
HOME | 사용자 홈 디렉토리를 의미합니다. |
PATH | 실행 파일의 경로입니다. 여러분이 chmod, mkdir 등의 명령어들은 /bin이나 /usr/bin, /sbin에 위치하는데, 이 경로들을 PATH 지정하면 여러분들은 굳이 /bin/chmod를 입력하지 않고, chmod 입력만 해주면 됩니다. |
LANG | 프로그램 실행 시 지원되는 언어를 말합니다. |
UID | 사용자의 UID입니다. |
SHELL | 사용자가 로그인시 실행되는 쉘을 말합니다. |
USER | 사용자의 계정 이름을 말합니다. |
FUNCNAME | 현재 실행되고 있는 함수 이름을 말합니다. |
TERM | 로그인 터미널을 말합니다. |
2) 배열(Array)
쉘 스크립트에서 배열은 1차원 배열만 지원하며 중괄호를 사용해야합니다. 배열 원소는 소괄호안에 공백으로 구분하여 줍니다. 배열 안 원소는 문자열이든 정수든 상관 없이 쓸 수 있는 특징이 있습니다.
#!/bin/bash
arr=("hello" "world" 1 2 3 4 5)
echo "배열 전체 : ${arr[@]}"
echo "배열 원소의 갯수 : ${#arr[@]}"
echo "배열 첫번째 : ${arr}, 혹은 ${arr[0]}"
echo "5번 index를 갖는 배열의 원소 : ${arr[5]}"
arr[5]="five"
echo "5번 index를 갖는 배열의 원소 : ${arr[5]}"
# 5번 원소 해제
unset arr[5]
echo "5번 원소 삭제 후"
echo "5번 index를 갖는 배열의 원소 : ${arr[5]}"
echo "6번 index를 갖는 배열의 원소 : ${arr[6]}"
3) 함수(Function)
다른 프로그래밍 언어와 같이 쉘 스크립트에서도 함수를 사용할 수 있습니다. 함수의 정의 형식은 아래와 같습니다. 함수명 앞 function은 써주지 않아도 무관합니다. 주의 할 점은 함수가 호출되기 전에 함수가 정의되어 있어야하며 호출할때는 괄호를 써주지 않고 호출해야한다는 점입니다.
function 함수명(){
... 내용 ...
}
#!/bin/bash
function func(){
echo "func()"
}
#함수 호출
func
4) 비교문
쉘스크립트에서 비교문은 약간 특이합니다. 아래와 같은 형식으로 비교문을 사용할 수 있습니다.
if [ 값1 조건 값2 ]; then
... 작업 내용 ...
fi
조건은 -eq, -gt, -lt 등이 있는데, 자세한 것은 아래의 코드에서 보도록 합시다.
#!/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
쉘스크립트는 명령어를 다루는 스크립트이기 때문에 파일이 존재하는지, 디렉토리가 존재하는지 등 파일과 관련된 조건 여부도 조건문을 통해 알 수 있습니다. 아래는 그것을 정리한 표입니다. 그리고 앞에 느낌표(!)를 써주고 조건을 기재하면 그 조건이 false일때 참이 성립합니다.
파일 관련 조건
조건 | 설명 |
if [ -d ${변수} ]; then if [ ! -d ${변수} ]; then |
${변수}의 디렉토리가 존재하면 참이 성립합니다. ${변수}의 디렉토리가 존재하지 않으면 참이 성립합니다. |
if [ -e ${변수} ]; then if [ ! -e ${변수} ]; then |
${변수}라는 파일이 존재하면 참입니다. ${변수}라는 파일이 존재하지 않으면 참입니다. |
if [ -L ${변수} ]; then | 파일이 symbolic link이면 참입니다. |
if [ -s ${변수} ]; then | 파일의 크기가 0보다 크면 참입니다. |
if [ -S ${변수} ]; then | 파일 타입이 소켓이면 참입니다. |
if [ -r ${변수} ]; then | 파일을 읽을 수 있으면 참입니다. |
if [ -w ${변수} ]; then | 파일을 쓸 수 있으면 참입니다. |
if [ -x ${변수} ]; then | 파일을 실행할 수 있으면 참입니다. |
if [ -f ${변수} ]; then | 파일이 정규 파일이면 참입니다. |
if [ -c ${변수} ]; then | 파일이 문자 장치이면 참입니다. |
if [ ${변수1} -nt ${변수2}]; then | 변수1의 파일이 변수2의 파일보다 최신 파일이면 참입니다. |
if [ ${변수1} -ot ${변수2}]; then | 변수1의 파일이 변수2의 파일보다 최신이 아니면 참입니다. |
if [ ${변수1} -ef ${변수2}]; then | 변수1의 파일과 변수2의 파일이 동일하면 참입니다. |
만약 AND조건과 OR조건을 섞어서 쓴다면 어떻게 할까요? AND는 -a, OR은 -o를 이용해서 조건을 추가할 수 있습니다.
function func(){
a=aa
b=bb
if [ -f ${a} -a -d ${b} ]; then
echo "a는 파일이고 b는 디렉토리 "
fi
}
#함수 호출
func
또한 여러 조건을 걸려면, 즉, C언어에서 else if역할을 하는 것은 elif라는 것을 사용하여 else if와 똑같은 역할을 할 수 있게 해줍니다. elif[ 조건 ]; then 이런식으로 쓰시면 됩니다.
만약 반복문에서 사용하여 조건이 참일때 반복문을 멈추고 싶을때 break라는 키워드를 사용하여 반복문을 멈출 수 있습니다.
C에서 switch case와 동일한 구문을 하는 문법도 존재합니다. 아래와 같이 case문을 사용하면 됩니다. 각 case의 끝을 보면 세미콜론 2개로 종료하는 것을 볼 수 있습니다.
#!/bin/bash
case ${1} in
"linux") echo "리눅스" ;;
"unix") echo "유닉스" ;;
"windows") echo "윈도우즈" ;;
"MacOS") echo "맥OS" ;;
*) echo "머야" ;;
esac
5) 반복문
반복문 중 for문에 대한 설명은 예제와 같이 보는 것으로 하겠습니다.
#!/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
6) 명령어의 종료 코드(exit)
만약 명령어가 실패할 경우 동작을 다르게 할것이라면 어떻게 할까요? 명령어의 종료 코드를 얻어오면 됩니다. 명령어의 종료 코드는 $?에 가장 최근 실행한 명령어의 종료 코드가 있습니다. 정상적인 종료는 0이 반환되는데, 만약 0이 아닌 값으로 반환되면 오류라고 판단하시면 됩니다. 여기에 간단한 오류를 내는 c파일(main.c)을 작성하고 gcc로 컴파일(gcc main.c)합니다. 이 c파일은 실행시 전달인자가 2개 보다 작으면 exit으로 코드 16을 반환합니다.
#include <stdlib.h>
#include <stdio.h>
int main(int argc, char* argv[]){
if(argc<2){
fprintf(stderr,"인자는 2개 이상이어야 합니다.\n");
exit(16);
}
}
그리고 쉘스크립트를 아래와 같이 수정해서 오류의 코드가 얼마인지 확인해보면 우리가 exit에 넘겨준 16이 찍히는 것을 확인할 수 있습니다.
#!/bin/bash
function func(){
./a.out
echo "오류 코드 ($?)"
}
#함수 호출
func
./a.out에 인자를 하나 넣어서 다시 실행시켜보세요. 0이 종료코드로 반환 될 것입니다. 이렇게 이전에 명령어의 결과를 봐야하는 경우 $?으로 판단하여 분기시킬 수 있습니다.
이렇게 종료 반환 코드는 명령어 뿐만 아니라 쉘 스크립트 자체도 반환할 수 있습니다. 즉, 쉘 스크립트의 수행이 실패했는지 알려주기 위해서는 exit 명령어를 사용하면 됩니다. 아래의 예가 그 사용법을 보여줍니다. 우선 exam2.sh라는 파일을 만들어봅시다. 이 쉘 스크립트 파일은 exam.sh에서 실행할 예정입니다. 별거없이 16의 종료코드를 반환하며 끝이 납니다.
#!/bin/bash
exit 16
이 쉘 파일을 exam.sh에서 실행시켜볼까요?
#!/bin/bash
function func(){
./exam2.sh
echo "오류 코드 ($?)"
}
#함수 호출
func
확인해보면 우리가 기재했던 16이 출력되는 것을 확인할 수 있습니다.
'컴퓨터 > 운영체제(주로 리눅스)' 카테고리의 다른 글
[리눅스] Inode와 링크(Hard, Symbolic Link) 개념과 이해 - ln 명령어 구현 소스 (0) | 2021.03.14 |
---|---|
[리눅스] /etc/passwd, /etc/shadow 파헤치기, 각 필드에 대한 설명 (0) | 2021.03.09 |
[리눅스] 커널(kernel)과 쉘(shell)의 개념, 쉘을 이해해보자 (0) | 2021.03.07 |
[운영체제] 스케줄링(Scheduling) 알고리즘(FIFO, SJF, 우선순위, Round-robin) (2) | 2021.03.01 |
[리눅스] cmake에 대한 개념 설명과 CMakeLists.txt 작성법 (0) | 2021.02.24 |