Red Hat Certified System Administrator (RHCSA) #3 셸 스크립트: 조건문, 반복, 인자, 종료 코드
#2 필수 도구에서 redirection,파이프,find,grep으로 셸을 다루는 손을 만들었다면, 이번 글에서는 그 명령들을 묶어 하나의 작업으로 자동화하는 셸 스크립트를 잡겠습니다. RHCSA 시험에는 “간단한 셸 스크립트를 작성하라"는 작업이 단골로 출제됩니다. 거창한 프로그래밍이 아니라, 인자를 받아 조건에 따라 분기하고, 여러 대상을 반복 처리하며, 종료 코드를 올바로 반환하는 30줄 안팎의 스크립트입니다.
그래서 이 글의 목표는 bash 문법 전체를 훑는 것이 아니라, 시험에서 손이 막히지 않을 만큼의 핵심 골격을 확실히 다지는 것입니다. shebang부터 함수까지, 시험에 실제로 나오는 요소만 추려 직접 쳐 보겠습니다.
shebang과 실행 #
스크립트의 첫 줄은 어떤 인터프리터로 실행할지 지정하는 shebang입니다. RHCSA에서는 bash 스크립트를 쓰므로 다음 한 줄로 시작합니다.
#!/usr/bin/bash
echo "Hello, RHCSA"작성한 파일은 실행 권한이 있어야 직접 실행됩니다. 권한을 부여하고 실행하는 흐름은 다음과 같습니다.
chmod +x hello.sh
./hello.sh권한을 주지 않았다면 bash hello.sh처럼 인터프리터에 직접 넘겨 실행할 수도 있습니다. 다만 시험에서 “실행 가능한 스크립트를 만들라"고 하면 chmod +x로 실행 권한을 남기는 편이 안전합니다.
스크립트를 작성하는 동안에는 bash -x script.sh로 실행하면 각 줄이 어떻게 전개되는지 보여 주므로, 동작이 의도와 다를 때 원인을 빠르게 찾을 수 있습니다.
변수와 인용 #
변수는 등호 양옆에 공백 없이 대입하고, 사용할 때는 $를 붙입니다. 값을 읽을 때 중괄호로 감싸면 변수명의 경계가 분명해집니다.
name="rhcsa"
count=3
echo "$name 시험은 ${count}번째 글입니다"인용 부호는 스크립트 안정성의 핵심입니다. 큰따옴표는 변수를 전개하고, 작은따옴표는 내부를 글자 그대로 둡니다.
dir="/etc/my dir"
ls "$dir" # 공백이 있어도 하나의 인자로 전달
ls $dir # 두 인자로 쪼개져 의도와 다르게 동작
echo '$name' # $name 자체를 출력변수를 사용할 때 항상 큰따옴표로 감싸는 습관을 들이면, 경로에 공백이 섞이거나 값이 비었을 때 생기는 오류를 막을 수 있습니다.
위치 인자 #
스크립트에 넘긴 인자는 $1, $2 순서로 받습니다. 전체 인자와 개수를 다루는 변수도 함께 알아 두겠습니다.
| 변수 | 의미 |
|---|---|
$0 | 스크립트 이름 |
$1, $2 | 첫 번째, 두 번째 위치 인자 |
$# | 전달된 인자의 개수 |
"$@" | 모든 인자를 각각 따옴표로 보존한 목록 |
$* | 모든 인자를 하나의 문자열로 결합 |
"$@"와 $*의 차이는 시험에서 반복 처리를 할 때 중요합니다. 인자 하나하나를 안전하게 순회하려면 "$@"를 큰따옴표로 감싸 사용합니다.
#!/usr/bin/bash
echo "스크립트: $0"
echo "인자 개수: $#"
echo "첫 인자: $1"
for arg in "$@"; do
echo "처리 대상: $arg"
done종료 코드 #
모든 명령은 끝나면서 종료 코드를 남깁니다. 0은 성공이고, 0이 아닌 값은 실패를 뜻합니다. 직전 명령의 종료 코드는 $?로 확인합니다.
ls /etc/passwd
echo "$?" # 0 (성공)
ls /no/such/path
echo "$?" # 2 (실패)스크립트는 exit로 자신의 종료 코드를 명시적으로 반환합니다. 채점 스크립트나 다른 명령이 이 값으로 성공,실패를 판단하므로, 실패 시 0이 아닌 값으로 종료하는 습관이 중요합니다.
if [[ -z "$1" ]]; then
echo "인자가 필요합니다" >&2
exit 1
fi
exit 0오류 메시지는 위 예시처럼 >&2로 표준 에러에 출력하는 것이 관례입니다.
test와 조건 검사 #
조건은 test 명령 또는 동치인 대괄호로 검사합니다. RHEL의 bash에서는 [[ ]]를 쓰는 편이 따옴표 처리에 안전합니다.
자주 쓰는 파일 검사 연산자는 다음과 같습니다.
| 연산자 | 참이 되는 조건 |
|---|---|
-e 파일 | 파일이 존재함 |
-f 파일 | 일반 파일임 |
-d 파일 | 디렉터리임 |
-r / -w / -x | 읽기 / 쓰기 / 실행 권한이 있음 |
-z 문자열 | 문자열이 비었음 |
-n 문자열 | 문자열이 비어 있지 않음 |
문자열과 숫자 비교는 연산자가 다릅니다. 문자열은 =,!=를, 숫자는 -eq,-ne,-lt,-le,-gt,-ge를 사용합니다.
[[ "$user" = "root" ]] # 문자열이 같은가
[[ "$count" -gt 5 ]] # 숫자가 5보다 큰가
[[ -f /etc/fstab ]] # 파일이 존재하는가if,elif,else #
조건 분기는 if로 작성합니다. then,fi로 블록을 닫는 형태를 정확히 외워 두겠습니다.
#!/usr/bin/bash
file="/etc/hosts"
if [[ -f "$file" ]]; then
echo "$file은 일반 파일입니다"
elif [[ -d "$file" ]]; then
echo "$file은 디렉터리입니다"
else
echo "$file을 찾을 수 없습니다"
fi조건 자리에는 대괄호뿐 아니라 명령 자체를 둘 수도 있습니다. 명령의 종료 코드가 0이면 참으로 취급되므로, 다음처럼 명령 성공 여부로 분기할 수 있습니다.
if grep -q "^myuser:" /etc/passwd; then
echo "사용자가 존재합니다"
ficase #
값이 여러 갈래로 나뉠 때는 case가 if,elif의 연쇄보다 읽기 쉽습니다. 각 분기는 )로 패턴을 적고 ;;로 닫습니다.
#!/usr/bin/bash
case "$1" in
start)
echo "서비스를 시작합니다" ;;
stop)
echo "서비스를 중지합니다" ;;
restart)
echo "서비스를 재시작합니다" ;;
*)
echo "사용법: $0 {start|stop|restart}" >&2
exit 1 ;;
esac마지막 *)는 어느 패턴에도 맞지 않은 경우를 받는 기본 분기입니다. 인자가 정해진 값 가운데 하나여야 할 때 유용합니다.
for,while,until 반복 #
반복은 시험에서 “여러 사용자를 한꺼번에 만들라"거나 “목록의 각 항목을 처리하라"는 작업에 직결됩니다.
for는 목록의 각 항목을 순회합니다.
for user in alice bob carol; do
echo "사용자 생성: $user"
done숫자 범위는 brace expansion이나 seq로 만듭니다.
for n in {1..5}; do
echo "디스크 $n"
donewhile은 조건이 참인 동안 반복하고, until은 조건이 참이 될 때까지 반복합니다. 파일을 한 줄씩 읽을 때 while read가 자주 쓰입니다.
while read -r line; do
echo "줄: $line"
done < /etc/hostnamecount=1
until [[ "$count" -gt 3 ]]; do
echo "시도 $count"
count=$((count + 1))
done명령 치환과 산술 연산 #
명령의 출력을 변수에 담을 때는 $(...)를 사용합니다. 백틱보다 중첩과 가독성에서 유리하므로 $(...)로 통일하겠습니다.
today=$(date +%F)
lines=$(wc -l < /etc/passwd)
echo "$today 기준 사용자 행 수: $lines"정수 산술은 $(( )) 안에서 수행합니다. 비교 조건도 (( ))로 쓸 수 있습니다.
a=7
b=3
echo "$(( a + b ))" # 10
echo "$(( a * b ))" # 21
if (( a > b )); then
echo "a가 더 큽니다"
firead로 입력 받기 #
스크립트가 사용자에게 값을 묻고 받을 때는 read를 씁니다. -p로 안내 문구를 함께 출력합니다.
#!/usr/bin/bash
read -p "사용자 이름을 입력하세요: " username
read -sp "비밀번호를 입력하세요: " password
echo
echo "입력한 사용자: $username"-s는 입력을 화면에 표시하지 않으므로 비밀번호 입력에 적합합니다. 다만 시험에서는 인자를 받는 방식이 더 흔하므로, read는 보조로 알아 두면 충분합니다.
함수 #
반복되는 작업은 함수로 묶습니다. 함수 안에서도 $1,$2로 함수에 넘긴 인자를 받습니다.
#!/usr/bin/bash
log() {
echo "[$(date +%T)] $1"
}
create_user() {
local name="$1"
if id "$name" &>/dev/null; then
log "$name은 이미 존재합니다"
return 1
fi
useradd "$name" && log "$name 생성 완료"
}
log "스크립트 시작"
create_user alice함수 안의 변수는 local로 선언해 함수 밖과 충돌하지 않게 두는 것이 안전합니다. 함수는 return으로 종료 코드를 돌려줍니다.
&&와 || #
명령을 연결하는 &&와 ||는 짧은 분기를 한 줄로 표현합니다. &&는 앞 명령이 성공했을 때, ||는 실패했을 때 뒤 명령을 실행합니다.
mkdir -p /data && echo "디렉터리 준비 완료"
id myuser &>/dev/null || useradd myuser위 두 번째 줄은 “사용자가 없으면 만든다"는 멱등한 작업을 한 줄로 표현한 것으로, 스크립트에서 자주 쓰는 형태입니다.
실전: 인자 검증과 반복 처리 스크립트 #
지금까지의 요소를 묶어, 인자로 받은 사용자들을 검증한 뒤 한꺼번에 만드는 스크립트를 작성하겠습니다. 시험에서 나오는 “스크립트 작성” 작업의 전형을 담은 예제입니다.
#!/usr/bin/bash
#
# 사용법: ./mkusers.sh user1 user2 ...
# 전달된 사용자들을 생성하고 결과를 종료 코드로 보고합니다.
# 1) 인자 검증: 하나도 없으면 사용법을 안내하고 실패로 종료
if [[ "$#" -eq 0 ]]; then
echo "사용법: $0 사용자명 [사용자명 ...]" >&2
exit 1
fi
# 2) root 권한 확인: useradd는 권한이 필요
if [[ "$(id -u)" -ne 0 ]]; then
echo "이 스크립트는 root로 실행해야 합니다" >&2
exit 1
fi
fail=0
# 3) 전달된 모든 인자를 순회하며 처리
for user in "$@"; do
if id "$user" &>/dev/null; then
echo "건너뜀: $user은 이미 존재합니다"
continue
fi
if useradd "$user"; then
echo "생성: $user"
else
echo "실패: $user 생성 중 오류" >&2
fail=$(( fail + 1 ))
fi
done
# 4) 실패가 하나라도 있으면 0이 아닌 코드로 종료
if (( fail > 0 )); then
echo "$fail 명 생성에 실패했습니다" >&2
exit 1
fi
echo "모든 사용자 처리 완료"
exit 0이 스크립트 하나에 이번 글의 핵심이 모두 들어 있습니다. $#로 인자 개수를 검증하고, "$@"로 인자를 안전하게 순회하며, if로 분기하고, continue로 다음 항목으로 넘어가며, 산술 (( ))로 실패 수를 세고, exit로 종료 코드를 명확히 반환합니다. 시험에서 요구하는 스크립트는 대개 이 골격의 변형입니다.
시험 포인트 #
- shebang
#!/usr/bin/bash로 시작하고,chmod +x로 실행 권한을 남긴다. 실행 가능한 스크립트를 만들라는 요구를 놓치지 않습니다. - 변수는 항상
"$var"로 따옴표를 감싼다. 공백,빈 값으로 인한 오작동을 막습니다. - 위치 인자
$1,"$@",$#를 정확히 구분한다. 반복 처리는for x in "$@"형태가 안전합니다. - 종료 코드는 성공 0, 실패는 0이 아닌 값으로
exit한다. 채점이 종료 코드를 보는 경우가 있습니다. if,then,fi,case,esac,for,do,done의 짝을 외운다. 닫는 키워드를 빠뜨리는 실수가 흔합니다.- 문법이 막히면
man bash의/CONDITIONAL절을 본다. 인터넷 없이 조건 연산자를 확인하는 길입니다. - 작성 중에는
bash -x script.sh로 전개 과정을 보며 디버깅한다.
정리 #
이번 글에서 잡은 것:
- shebang,변수 인용,위치 인자. 스크립트의 입력과 골격을 만드는 토대
- 종료 코드(
$?,exit). 성공,실패를 0과 0 아닌 값으로 알리는 약속 - 조건(
test,[[ ]],if,case)과 반복(for,while,until). 분기와 순회의 핵심 구문 - 명령 치환
$(),산술(( )),read,함수,&&,||. 스크립트를 실용적으로 만드는 도구 - 실전 스크립트. 인자를 검증하고 여러 대상을 반복 처리하며 종료 코드로 결과를 보고하는 전형
RHCSA의 스크립트 작업은 화려한 문법이 아니라, 인자를 받아 조건에 따라 처리하고 결과를 올바로 반환하는 기본기로 풀립니다. 위 골격을 손에 익혀 두면 시험에서 막히지 않습니다.
다음: 부팅과 시스템 #
셸과 스크립트라는 작업의 토대를 잡았습니다. 이제 시스템 자체가 켜지고 동작하는 방식으로 들어갑니다.
#4 부팅과 시스템: systemd, target, GRUB2, password recovery에서는 systemd가 부팅을 이끄는 흐름과 target 전환, GRUB2 부트로더 설정, 그리고 시험 단골인 root 비밀번호 복구 절차까지 직접 따라 하며 정리하겠습니다.