쉘 스크립트 Part 3: 제어문 - if, case, for, while, until
개요
쉘 스크립트의 제어문은 프로그램의 흐름을 제어하는 핵심 요소입니다. 이 문서에서는 조건문(if, case)과 반복문(for, while, until), 그리고 흐름 제어(continue, break)를 학습합니다. 이를 통해 복잡한 로직을 구현하고 자동화 스크립트를 작성할 수 있습니다.
1. if 문
1.1 기본 구조
쉘 프로그램은 라인(행) 단위로 해석되므로 각각 다른 라인에 작성해야 하지만 ;을 사용해 라인의 끝을 인식시킬 수 있습니다.
기본 형식:
if [ 조건 ]; then
명령어
fi
중요: if로 시작해서 fi로 닫아주는 것이 중요합니다.
1.2 if ~ else 문
if [ 조건 ]; then
명령어1
else
명령어2
fi
1.3 if ~ elif ~ else 문
if [ 조건1 ]; then
명령어1
elif [ 조건2 ]; then
명령어2
else
명령어3
fi
1.4 실전 예제
예제 1: 숫자 비교
#!/bin/bash
read -p "숫자를 입력하세요: " num
if [ $num -gt 0 ]; then
echo "양수입니다"
elif [ $num -lt 0 ]; then
echo "음수입니다"
else
echo "0입니다"
fi
예제 2: 파일 존재 확인
#!/bin/bash
read -p "파일명을 입력하세요: " filename
if [ -f "$filename" ]; then
echo "$filename 파일이 존재합니다"
ls -lh "$filename"
else
echo "$filename 파일이 존재하지 않습니다"
fi
예제 3: 사용자 권한 확인
#!/bin/bash
if [ "$(id -u)" -eq 0 ]; then
echo "root 권한으로 실행 중입니다"
else
echo "일반 사용자 권한입니다"
echo "root 권한이 필요합니다"
exit 1
fi
1.5 한 줄 if 문
# 형식
[ 조건 ] && 참일_때_명령어 || 거짓일_때_명령어
# 예시
[ -f /etc/passwd ] && echo "파일 존재" || echo "파일 없음"
2. case 문
2.1 기본 구조
case문은 여러 조건을 처리할 때 if문보다 가독성이 좋습니다.
case 변수 in
패턴1)
명령어1
;;
패턴2)
명령어2
;;
*)
기본_명령어
;;
esac
중요 포인트:
exit는 C언어에서break와 같음,;;로 세미콜론 두 개로 닫아줌*는 C언어에서default와 같음- case로 시작하고 esac으로 닫음 (case를 거꾸로)
2.2 실전 예제
예제 1: 간단한 메뉴
#!/bin/bash
echo "=== 메뉴 선택 ==="
echo "1. 파일 목록"
echo "2. 현재 디렉토리"
echo "3. 시스템 정보"
echo "4. 종료"
read -p "선택하세요 (1-4): " choice
case $choice in
1)
echo "=== 파일 목록 ==="
ls -l
;;
2)
echo "=== 현재 디렉토리 ==="
pwd
;;
3)
echo "=== 시스템 정보 ==="
uname -a
;;
4)
echo "프로그램을 종료합니다"
exit 0
;;
*)
echo "잘못된 선택입니다"
;;
esac
예제 2: 파일 확장자 처리
#!/bin/bash
read -p "파일명을 입력하세요: " filename
case "$filename" in
*.txt)
echo "텍스트 파일입니다"
cat "$filename"
;;
*.sh)
echo "쉘 스크립트 파일입니다"
chmod +x "$filename"
echo "실행 권한을 부여했습니다"
;;
*.log)
echo "로그 파일입니다"
tail -n 20 "$filename"
;;
*)
echo "알 수 없는 파일 형식입니다"
;;
esac
예제 3: 다중 패턴 매칭
#!/bin/bash
read -p "명령어를 입력하세요: " cmd
case "$cmd" in
start|START|Start)
echo "서비스를 시작합니다"
;;
stop|STOP|Stop)
echo "서비스를 중지합니다"
;;
restart|RESTART|Restart)
echo "서비스를 재시작합니다"
;;
status|STATUS|Status)
echo "서비스 상태를 확인합니다"
;;
*)
echo "사용법: $0 {start|stop|restart|status}"
exit 1
;;
esac
3. for 문
3.1 기본 구조
for문은 지정된 목록의 각 항목에 대해 명령을 반복 실행합니다.
for 변수 in 값1 값2 값3
do
명령어
done
특징:
- 반복문은 in 다음에 나오는 변수의 값을 순차적으로 변수에 대입 및 반복 실행
- 변수 값의 개수만큼 루프를 돌면서 do 다음의 실행문을 실행하고, done으로 마무리
3.2 seq를 이용한 범위 반복
# seq 명령어를 이용한 순차적 증가
for i in $(seq 1 10)
do
echo "숫자: $i"
done
seq 옵션:
seq 시작값 끝값: 시작값부터 끝값까지seq 시작값 증가값 끝값: 시작값부터 증가값만큼 증가하여 끝값까지
# 1부터 10까지 2씩 증가
for i in $(seq 1 2 10)
do
echo "$i"
done
# 출력: 1 3 5 7 9
3.3 C 스타일 for 문
# 형식
for (( 초기값; 조건; 증가값 ))
do
명령어
done
# 예시
for (( i=1; i<=10; i++ ))
do
echo "$i"
done
3.4 실전 예제
예제 1: 파일 목록 처리
#!/bin/bash
echo "=== 현재 디렉토리의 .txt 파일 ==="
for file in *.txt
do
if [ -f "$file" ]; then
echo "파일: $file"
echo "크기: $(du -h "$file" | cut -f1)"
echo "---"
fi
done
예제 2: 디렉토리 백업
#!/bin/bash
BACKUP_DIR="/backup"
DATE=$(date +%Y%m%d)
for dir in /home/user1 /home/user2 /home/user3
do
if [ -d "$dir" ]; then
echo "백업 중: $dir"
tar -czf "${BACKUP_DIR}/$(basename $dir)_${DATE}.tar.gz" "$dir"
echo "완료: $dir"
else
echo "디렉토리 없음: $dir"
fi
done
예제 3: 구구단 출력
#!/bin/bash
read -p "구구단 단수를 입력하세요: " dan
echo "=== ${dan}단 ==="
for (( i=1; i<=9; i++ ))
do
result=$((dan * i))
echo "${dan} x ${i} = ${result}"
done
예제 4: 여러 서버 Ping 테스트
#!/bin/bash
servers="8.8.8.8 1.1.1.1 192.168.1.1"
for server in $servers
do
echo "Testing $server..."
if ping -c 1 -W 2 $server &> /dev/null; then
echo "✓ $server is reachable"
else
echo "✗ $server is unreachable"
fi
done
4. while 문
4.1 기본 구조
while문은 조건이 참인 동안 반복 실행합니다.
while [ 조건 ]
do
명령어
done
4.2 실전 예제
예제 1: 카운트다운
#!/bin/bash
count=10
while [ $count -gt 0 ]
do
echo "카운트다운: $count"
sleep 1
count=$((count - 1))
done
echo "완료!"
예제 2: 파일 읽기
#!/bin/bash
# 방법 1: while read 사용
while read line
do
echo "라인: $line"
done < /etc/passwd
# 방법 2: 파일에서 특정 정보 추출
echo "=== 사용자 목록 ==="
while IFS=: read username password uid gid info home shell
do
if [ $uid -ge 1000 ]; then
echo "사용자: $username, UID: $uid, 홈: $home"
fi
done < /etc/passwd
예제 3: 무한 루프 (서비스 모니터링)
#!/bin/bash
echo "서비스 모니터링 시작 (Ctrl+C로 종료)"
while true
do
if systemctl is-active --quiet httpd; then
echo "[$(date '+%Y-%m-%d %H:%M:%S')] Apache 서비스 정상"
else
echo "[$(date '+%Y-%m-%d %H:%M:%S')] Apache 서비스 중지됨! 재시작 시도..."
systemctl start httpd
fi
sleep 60 # 60초 대기
done
예제 4: 사용자 입력 검증
#!/bin/bash
valid_input=0
while [ $valid_input -eq 0 ]
do
read -p "1에서 10 사이의 숫자를 입력하세요: " num
if [[ "$num" =~ ^[0-9]+$ ]]; then
if [ $num -ge 1 ] && [ $num -le 10 ]; then
echo "올바른 입력입니다: $num"
valid_input=1
else
echo "범위를 벗어났습니다. 다시 시도하세요."
fi
else
echo "숫자가 아닙니다. 다시 시도하세요."
fi
done
5. until 문
5.1 기본 구조
until문은 while문과 반대로 조건이 거짓인 동안 반복 실행합니다. 조건이 참이 되면 종료됩니다.
until [ 조건 ]
do
명령어
done
5.2 실전 예제
예제 1: 파일 생성 대기
#!/bin/bash
filename="/tmp/test.txt"
echo "파일 생성을 기다리는 중: $filename"
until [ -f "$filename" ]
do
echo "대기 중..."
sleep 2
done
echo "파일이 생성되었습니다!"
예제 2: 서버 응답 대기
#!/bin/bash
server="192.168.1.100"
echo "서버 응답 대기: $server"
until ping -c 1 -W 2 $server &> /dev/null
do
echo "[$(date '+%H:%M:%S')] 서버 응답 없음, 재시도..."
sleep 5
done
echo "서버가 응답합니다!"
예제 3: 디스크 사용량 모니터링
#!/bin/bash
threshold=80
echo "디스크 사용량 모니터링 시작 (임계값: ${threshold}%)"
until [ $(df / | tail -1 | awk '{print $5}' | sed 's/%//') -ge $threshold ]
do
usage=$(df / | tail -1 | awk '{print $5}')
echo "[$(date '+%H:%M:%S')] 현재 사용량: $usage"
sleep 300 # 5분 대기
done
echo "경고! 디스크 사용량이 ${threshold}%를 초과했습니다!"
6. continue와 break
6.1 continue
continue는 현재 반복을 건너뛰고 다음 반복으로 이동합니다.
#!/bin/bash
for i in {1..10}
do
# 짝수는 건너뛰기
if [ $((i % 2)) -eq 0 ]; then
continue
fi
echo "홀수: $i"
done
# 출력: 1, 3, 5, 7, 9
예제: 특정 파일 제외하고 처리
#!/bin/bash
for file in *
do
# .git 디렉토리는 건너뛰기
if [ "$file" = ".git" ]; then
continue
fi
echo "처리 중: $file"
done
6.2 break
break는 반복문을 완전히 종료합니다.
#!/bin/bash
count=1
while true
do
echo "카운트: $count"
if [ $count -ge 5 ]; then
echo "5에 도달하여 종료"
break
fi
count=$((count + 1))
done
예제: 특정 조건 만족 시 검색 중단
#!/bin/bash
read -p "검색할 사용자명: " search_user
found=0
while IFS=: read username rest
do
if [ "$username" = "$search_user" ]; then
echo "사용자를 찾았습니다: $username"
found=1
break
fi
done < /etc/passwd
if [ $found -eq 0 ]; then
echo "사용자를 찾지 못했습니다: $search_user"
fi
6.3 sleep 명령어
sleep은 스크립트 실행을 일시 정지합니다.
# 형식
sleep 숫자[단위]
# 단위:
# s : 초 (기본값)
# m : 분
# h : 시간
# d : 일
# 예시
sleep 1 # 1초 대기
sleep 1s # 1초 대기
sleep 1m # 1분 대기
sleep 0.5 # 0.5초 대기
7. 종합 예제
예제 1: 시스템 모니터링 스크립트
#!/bin/bash
echo "=== 시스템 모니터링 스크립트 ==="
echo ""
while true
do
clear
echo "=========================================="
echo "시스템 모니터링 - $(date '+%Y-%m-%d %H:%M:%S')"
echo "=========================================="
echo ""
# CPU 사용률
cpu_usage=$(top -bn1 | grep "Cpu(s)" | awk '{print $2}' | cut -d'%' -f1)
echo "CPU 사용률: ${cpu_usage}%"
# 메모리 사용률
mem_usage=$(free | grep Mem | awk '{printf "%.2f", $3/$2 * 100}')
echo "메모리 사용률: ${mem_usage}%"
# 디스크 사용률
disk_usage=$(df / | tail -1 | awk '{print $5}')
echo "디스크 사용률: $disk_usage"
echo ""
echo "다음 갱신까지 10초... (Ctrl+C로 종료)"
sleep 10
done
예제 2: 백업 자동화 스크립트
#!/bin/bash
BACKUP_SOURCE="/home/user/important"
BACKUP_DEST="/backup"
DATE=$(date +%Y%m%d_%H%M%S)
KEEP_DAYS=7
echo "=== 백업 스크립트 시작 ==="
# 백업 디렉토리 확인
if [ ! -d "$BACKUP_DEST" ]; then
echo "백업 디렉토리 생성: $BACKUP_DEST"
mkdir -p "$BACKUP_DEST"
fi
# 백업 실행
BACKUP_FILE="${BACKUP_DEST}/backup_${DATE}.tar.gz"
echo "백업 중: $BACKUP_SOURCE -> $BACKUP_FILE"
if tar -czf "$BACKUP_FILE" "$BACKUP_SOURCE" 2>/dev/null; then
echo "✓ 백업 성공: $BACKUP_FILE"
# 백업 파일 크기 확인
size=$(du -h "$BACKUP_FILE" | cut -f1)
echo " 백업 크기: $size"
else
echo "✗ 백업 실패"
exit 1
fi
# 오래된 백업 파일 삭제
echo ""
echo "오래된 백업 파일 정리 (${KEEP_DAYS}일 이상)"
find "$BACKUP_DEST" -name "backup_*.tar.gz" -type f -mtime +${KEEP_DAYS} | while read old_file
do
echo "삭제: $old_file"
rm -f "$old_file"
done
echo ""
echo "=== 백업 완료 ==="
예제 3: 사용자 관리 메뉴 스크립트
#!/bin/bash
while true
do
clear
echo "======================================"
echo " 사용자 관리 시스템"
echo "======================================"
echo "1. 사용자 목록 보기"
echo "2. 사용자 추가"
echo "3. 사용자 삭제"
echo "4. 사용자 정보 보기"
echo "5. 종료"
echo "======================================"
read -p "선택하세요 (1-5): " choice
case $choice in
1)
echo ""
echo "=== 시스템 사용자 목록 ==="
awk -F: '$3 >= 1000 {printf "%-15s UID: %-5s Home: %s\n", $1, $3, $6}' /etc/passwd
;;
2)
read -p "생성할 사용자명: " new_user
if id "$new_user" &>/dev/null; then
echo "오류: 이미 존재하는 사용자입니다"
else
sudo useradd -m "$new_user"
sudo passwd "$new_user"
echo "사용자 생성 완료: $new_user"
fi
;;
3)
read -p "삭제할 사용자명: " del_user
if id "$del_user" &>/dev/null; then
read -p "정말 삭제하시겠습니까? (y/n): " confirm
if [ "$confirm" = "y" ]; then
sudo userdel -r "$del_user"
echo "사용자 삭제 완료: $del_user"
fi
else
echo "오류: 존재하지 않는 사용자입니다"
fi
;;
4)
read -p "확인할 사용자명: " check_user
if id "$check_user" &>/dev/null; then
echo ""
id "$check_user"
echo ""
grep "^${check_user}:" /etc/passwd
else
echo "오류: 존재하지 않는 사용자입니다"
fi
;;
5)
echo "프로그램을 종료합니다"
exit 0
;;
*)
echo "잘못된 선택입니다"
;;
esac
echo ""
read -p "계속하려면 Enter를 누르세요..."
done
주요 개념 요약
| 제어문 | 용도 | 시작/종료 | 특징 |
|---|---|---|---|
| if | 조건 분기 | if … fi |
조건이 참일 때 실행 |
| case | 다중 조건 분기 | case … esac |
패턴 매칭, ;;로 종료 |
| for | 목록 반복 | for … done |
지정된 목록만큼 반복 |
| while | 조건 반복 (참) | while … done |
조건이 참인 동안 반복 |
| until | 조건 반복 (거짓) | until … done |
조건이 거짓인 동안 반복 |
| continue | 반복 건너뛰기 | - | 현재 반복을 건너뜀 |
| break | 반복 종료 | - | 반복문을 완전히 종료 |
학습 포인트
1. if vs case
- if: 복잡한 조건식, 범위 비교에 적합
- case: 고정된 값 매칭, 메뉴 선택에 적합
2. for vs while
- for: 반복 횟수가 정해진 경우
- while: 조건에 따라 반복 횟수가 가변적인 경우
3. while vs until
- while: “~하는 동안” (조건이 참일 때)
- until: “~할 때까지” (조건이 거짓일 때)
4. 실무 팁
세미콜론(;) 활용:
# 여러 줄을 한 줄로
if [ -f file.txt ]; then cat file.txt; fi
# 가독성을 위해 여러 줄 권장
if [ -f file.txt ]; then
cat file.txt
fi
무한 루프 작성:
# 방법 1
while true; do
# 작업
done
# 방법 2
while :; do
# 작업
done
# 방법 3
for (( ; ; )); do
# 작업
done
안전한 스크립트 작성:
#!/bin/bash
# 에러 시 즉시 종료
set -e
# 정의되지 않은 변수 사용 시 에러
set -u
# 파이프라인 에러 전파
set -o pipefail
마무리
쉘 스크립트의 제어문은 자동화의 핵심입니다. if와 case로 조건을 처리하고, for/while/until로 반복 작업을 수행하며, continue/break로 흐름을 제어할 수 있습니다.
실무에서는 에러 처리, 사용자 입력 검증, 로그 기록을 함께 구현하여 안정적인 스크립트를 작성해야 합니다. 다음 Part에서는 함수를 학습하여 코드 재사용성을 높이는 방법을 배웁니다.
댓글 남기기