목차
쉘 스크립트로 업무 자동화
🎯 목표: 반복 작업을 자동화하여 생산성을 극대화하기
⏰ 예상 시간: 3-4시간
📋 준비물: - 기본 명령어와 고급 명령어에 익숙한 상태 - 터미널 접근 권한 - 텍스트 에디터 (nano, vim 등)
반복 작업에 지치셨나요? 쉘 스크립트로 모든 걸 자동화해보세요! 한 번 만들어두면 평생 써먹을 수 있는 자동화 스크립트를 만들어보겠습니다.
🎯 학습 목표
이 가이드를 마치면:
- 쉘 스크립트의 기본 문법을 이해할 수 있습니다
- 조건문과 반복문을 활용한 논리적 프로그래밍이 가능합니다
- 실무에서 바로 쓸 수 있는 자동화 스크립트를 만들 수 있습니다
- 크론탭으로 정기적으로 실행되는 작업을 설정할 수 있습니다
🚀 1단계: 쉘 스크립트 첫 만남
쉘 스크립트가 뭔가요?
쉘 스크립트는 Linux 명령어들을 파일에 저장해서 한 번에 실행할 수 있게 해주는 프로그램입니다.
예시:
# 매일 하는 작업들 pwd ls -la df -h ps aux | grep apache
이런 명령어들을 매번 타이핑하는 대신, 스크립트 파일에 저장해두고 한 번에 실행할 수 있습니다!
쉘 스크립트의 기본 구조
1. 셔뱅(Shebang) - 필수!
#!/bin/bash
- 의미: 이 파일이 bash 쉘로 실행되어야 함을 알림 - 위치: 파일의 첫 번째 줄에 반드시 작성 - 다른 쉘들:
#!/bin/sh
- 기본 쉘#!/usr/bin/python3
- Python 스크립트#!/usr/bin/perl
- Perl 스크립트
2. 주석(Comment) - 설명을 위한 메모
# 이것은 한 줄 주석입니다 echo "Hello World" # 이 줄 끝에도 주석 가능 # 여러 줄 주석은 이렇게 # 각 줄마다 #을 붙여야 합니다
3. 명령어 실행
# 각 줄은 하나의 명령어 echo "첫 번째 명령어" ls -la pwd
4. 스크립트 종료
- 스크립트는 마지막 명령어까지 실행 후 자동 종료
exit 0
- 정상 종료 (선택사항)exit 1
- 에러로 종료 (선택사항)
첫 번째 스크립트 만들기
1. 스크립트 파일 생성:
nano hello.sh
2. 스크립트 내용 작성:
#!/bin/bash # 이것은 주석입니다 echo "안녕하세요! 첫 번째 쉘 스크립트입니다." echo "현재 시간: $(date)" echo "현재 위치: $(pwd)"
3. 실행 권한 부여:
chmod +x hello.sh
4. 스크립트 실행:
./hello.sh
🔍 이해하기:
#!/bin/bash
: 쉘 스크립트임을 알리는 셔뱅(shebang)#
: 주석 (설명)echo
: 텍스트 출력$(명령어)
: 명령어 실행 결과를 변수로 사용
💡 초보자 팁:
- 스크립트 파일명은
.sh
확장자를 붙이는 것이 관례 - 파일명에 공백이나 특수문자 사용 금지
- 실행 권한은 한 번만 부여하면 됨 (chmod +x)
- 스크립트 실행 시
./
를 앞에 붙여야 함
📝 2단계: 변수와 사용자 입력
변수 사용하기
변수란? 데이터를 저장하는 상자라고 생각하세요!
#!/bin/bash # 변수 선언 (= 앞뒤에 공백 없이!) NAME="홍길동" AGE=25 TODAY=$(date +%Y-%m-%d) # 변수 사용 ($ 붙이기) echo "이름: $NAME" echo "나이: $AGE" echo "오늘 날짜: $TODAY" # 중괄호로 변수 구분하기 echo "안녕하세요 ${NAME}님!"
🔍 변수 문법 상세 설명:
1. 변수 선언 규칙:
- 공백 금지:
NAME=“홍길동”
(O),NAME = “홍길동”
(X) - 따옴표 사용: 공백이 있는 값은 따옴표로 감싸기
- 숫자는 따옴표 없이:
AGE=25
(O),AGE=“25”
(O, 하지만 불필요)
2. 변수 사용 방법:
- 기본:
$변수명
-echo $NAME
- 중괄호:
${변수명}
-echo ${NAME}님
- 중괄호가 필요한 경우:
# 변수명이 명확하지 않을 때 echo "${NAME}님" # $NAME님 (X) - 변수명이 NAME님으로 인식됨
3. 특별한 변수들:
#!/bin/bash echo "스크립트 이름: $0" echo "첫 번째 인수: $1" echo "모든 인수: $@" echo "인수 개수: $#" echo "현재 프로세스 ID: $$" echo "마지막 명령어 결과: $?"
4. 변수 타입별 예시:
#!/bin/bash # 문자열 변수 NAME="홍길동" CITY="서울" # 숫자 변수 AGE=25 COUNT=10 # 명령어 결과를 변수에 저장 CURRENT_TIME=$(date) FILE_COUNT=$(ls | wc -l) SYSTEM_INFO=$(uname -a) # 배열 변수 (고급) FRUITS=("사과" "바나나" "오렌지") echo "첫 번째 과일: ${FRUITS[0]}"
사용자 입력 받기
read 명령어란? 사용자로부터 키보드 입력을 받는 명령어입니다.
#!/bin/bash # 사용자 입력 받기 echo "이름을 입력하세요:" read NAME echo "나이를 입력하세요:" read AGE echo "안녕하세요 ${NAME}님! ${AGE}세이시군요."
한 줄로 입력받기:
#!/bin/bash read -p "이름을 입력하세요: " NAME read -p "나이를 입력하세요: " AGE echo "안녕하세요 ${NAME}님! ${AGE}세이시군요."
🔍 read 명령어 상세 설명:
1. 기본 문법:
read 변수명
2. 옵션들:
- -p “메시지”: 프롬프트 메시지와 함께 입력받기
- -s: 비밀번호처럼 입력 내용을 화면에 표시하지 않기
- -t 초: 시간 제한 (초 단위)
- -n 문자수: 지정된 문자수만 입력받기
3. 다양한 입력 방법:
#!/bin/bash # 기본 입력 read NAME # 프롬프트와 함께 read -p "이름: " NAME # 비밀번호 입력 (화면에 표시 안됨) read -s -p "비밀번호: " PASSWORD echo # 줄바꿈 # 시간 제한 (5초) read -t 5 -p "5초 안에 입력: " QUICK_INPUT # 여러 변수에 한 번에 입력 read -p "이름과 나이: " NAME AGE # 배열에 입력 read -a FRUITS -p "과일들 (공백으로 구분): " echo "첫 번째 과일: ${FRUITS[0]}"
4. 입력 검증 예시:
#!/bin/bash # 숫자 입력 확인 read -p "나이를 입력하세요: " AGE # 숫자인지 확인 if [[ "$AGE" =~ ^[0-9]+$ ]]; then echo "올바른 나이입니다: $AGE" else echo "숫자를 입력해주세요!" exit 1 fi # 빈 입력 확인 read -p "이름을 입력하세요: " NAME if [[ -z "$NAME" ]]; then echo "이름을 입력해주세요!" exit 1 fi
명령줄 인수 사용하기
명령줄 인수란? 스크립트 실행 시 함께 전달하는 값들입니다.
#!/bin/bash # 스크립트 실행 시 전달받은 인수들 echo "스크립트 이름: $0" echo "첫 번째 인수: $1" echo "두 번째 인수: $2" echo "모든 인수: $@" echo "인수 개수: $#"
실행 방법:
./script.sh 안녕 하세요
🔍 명령줄 인수 상세 설명:
1. 특별한 변수들:
- $0: 스크립트 파일명
- $1, $2, $3…: 첫 번째, 두 번째, 세 번째 인수
- $@: 모든 인수를 배열로
- $#: 인수의 개수
- $*: 모든 인수를 하나의 문자열로
2. 인수 처리 예시:
#!/bin/bash echo "전달받은 인수들:" echo "스크립트명: $0" echo "인수 개수: $#" # 각 인수 출력 for i in {1..$#}; do echo "인수 $i: ${!i}" done # 모든 인수 한 번에 출력 echo "모든 인수: $@"
3. 인수 검증:
#!/bin/bash # 인수가 없으면 도움말 출력 if [ $# -eq 0 ]; then echo "사용법: $0 <파일명> [옵션]" echo "예시: $0 test.txt -v" exit 1 fi # 최소 인수 개수 확인 if [ $# -lt 2 ]; then echo "에러: 최소 2개의 인수가 필요합니다." exit 1 fi # 특정 인수 확인 if [ "$1" = "-h" ] || [ "$1" = "--help" ]; then echo "도움말: 이 스크립트는 파일을 처리합니다." exit 0 fi
💡 실습해보기:
#!/bin/bash # backup.sh # 인수 검증 if [ $# -eq 0 ]; then echo "사용법: $0 <백업할폴더>" exit 1 fi # 폴더 존재 확인 if [ ! -d "$1" ]; then echo "에러: '$1' 폴더가 존재하지 않습니다." exit 1 fi BACKUP_DIR=$1 BACKUP_NAME="backup_$(date +%Y%m%d_%H%M%S).tar.gz" echo "백업 시작: $BACKUP_DIR" tar -czf $BACKUP_NAME $BACKUP_DIR echo "백업 완료: $BACKUP_NAME"
4. 옵션 처리 (고급):
#!/bin/bash # 옵션 변수 초기화 VERBOSE=false FORCE=false # 옵션 처리 while [[ $# -gt 0 ]]; do case $1 in -v|--verbose) VERBOSE=true shift ;; -f|--force) FORCE=true shift ;; -h|--help) echo "사용법: $0 [-v] [-f] <파일명>" exit 0 ;; *) FILE="$1" shift ;; esac done echo "파일: $FILE" echo "상세출력: $VERBOSE" echo "강제실행: $FORCE"
🔀 3단계: 조건문으로 논리 만들기
if 문 기본 사용법
조건문이란? “만약 ~라면 ~하고, 아니면 ~해라”를 프로그래밍으로 표현한 것입니다.
#!/bin/bash read -p "나이를 입력하세요: " AGE if [ $AGE -ge 18 ]; then echo "성인입니다." else echo "미성년자입니다." fi
🔍 if 문 문법 상세 설명:
1. 기본 구조:
if [ 조건 ]; then # 조건이 참일 때 실행할 명령어들 elif [ 다른조건 ]; then # 다른 조건이 참일 때 실행할 명령어들 else # 모든 조건이 거짓일 때 실행할 명령어들 fi
2. 조건 연산자 (숫자 비교):
- -eq: 같음 (equal) -
[ $a -eq $b ]
- -ne: 다름 (not equal) -
[ $a -ne $b ]
- -gt: 큼 (greater than) -
[ $a -gt $b ]
- -ge: 크거나 같음 (greater equal) -
[ $a -ge $b ]
- -lt: 작음 (less than) -
[ $a -lt $b ]
- -le: 작거나 같음 (less equal) -
[ $a -le $b ]
3. 조건 연산자 (문자열 비교):
- =: 같음 -
[ “$a” = “$b” ]
- !=: 다름 -
[ “$a” != “$b” ]
- -z: 빈 문자열인지 확인 -
[ -z “$a” ]
- -n: 빈 문자열이 아닌지 확인 -
[ -n “$a” ]
4. 조건 연산자 (파일/디렉토리):
- -f: 파일인지 확인 -
[ -f “파일명” ]
- -d: 디렉토리인지 확인 -
[ -d “디렉토리명” ]
- -e: 존재하는지 확인 -
[ -e “파일명” ]
- -r: 읽기 가능한지 확인 -
[ -r “파일명” ]
- -w: 쓰기 가능한지 확인 -
[ -w “파일명” ]
- -x: 실행 가능한지 확인 -
[ -x “파일명” ]
5. 논리 연산자:
- &&: AND (그리고) -
[ 조건1 ] && [ 조건2 ]
- ||: OR (또는) -
[ 조건1 ] || [ 조건2 ]
- !: NOT (아님) -
[ ! 조건 ]
6. 다양한 if 문 예시:
#!/bin/bash # 기본 if-else if [ $AGE -ge 18 ]; then echo "성인입니다." else echo "미성년자입니다." fi # if-elif-else if [ $SCORE -ge 90 ]; then echo "A등급" elif [ $SCORE -ge 80 ]; then echo "B등급" elif [ $SCORE -ge 70 ]; then echo "C등급" else echo "D등급" fi # 복합 조건 if [ $AGE -ge 18 ] && [ "$GENDER" = "남성" ]; then echo "성인 남성입니다." fi # 파일 존재 확인 if [ -f "$FILE" ]; then echo "파일이 존재합니다." else echo "파일이 없습니다." fi # 문자열 비교 if [ "$NAME" = "홍길동" ]; then echo "홍길동님 안녕하세요!" fi # 빈 문자열 확인 if [ -z "$INPUT" ]; then echo "입력값이 없습니다." fi
7. 대괄호 vs 이중 대괄호:
#!/bin/bash # 기본 대괄호 (POSIX 호환) if [ $AGE -ge 18 ]; then echo "성인" fi # 이중 대괄호 (Bash 확장 기능) if [[ $AGE -ge 18 ]]; then echo "성인" fi # 이중 대괄호의 장점 if [[ "$STRING" =~ ^[0-9]+$ ]]; then echo "숫자입니다" fi
문자열 비교
문자열 비교란? 텍스트 값들이 같은지, 다른지, 비어있는지 등을 확인하는 방법입니다.
#!/bin/bash read -p "비밀번호를 입력하세요: " PASSWORD if [ "$PASSWORD" = "secret123" ]; then echo "로그인 성공!" elif [ "$PASSWORD" = "admin" ]; then echo "관리자 모드" else echo "비밀번호가 틀렸습니다." fi
🔍 문자열 비교 상세 설명:
1. 기본 비교 연산자:
- =: 같음 -
[ “$a” = “$b” ]
- !=: 다름 -
[ “$a” != “$b” ]
- -z: 빈 문자열인지 확인 -
[ -z “$a” ]
- -n: 빈 문자열이 아닌지 확인 -
[ -n “$a” ]
2. 문자열 비교 예시:
#!/bin/bash # 기본 비교 NAME="홍길동" if [ "$NAME" = "홍길동" ]; then echo "홍길동님 안녕하세요!" fi # 대소문자 구분 비교 if [ "$NAME" = "홍길동" ]; then echo "정확히 일치합니다" fi # 빈 문자열 확인 INPUT="" if [ -z "$INPUT" ]; then echo "입력값이 비어있습니다" fi # 문자열이 있는지 확인 if [ -n "$NAME" ]; then echo "이름이 입력되었습니다: $NAME" fi # 여러 조건 조합 if [ -n "$NAME" ] && [ "$NAME" != "admin" ]; then echo "일반 사용자입니다" fi
3. 문자열 패턴 매칭 (고급):
#!/bin/bash # 이중 대괄호에서 정규표현식 사용 EMAIL="[email protected]" if [[ "$EMAIL" =~ ^[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,}$ ]]; then echo "올바른 이메일 형식입니다" else echo "잘못된 이메일 형식입니다" fi # 문자열 포함 여부 확인 TEXT="Hello World" if [[ "$TEXT" == *"World"* ]]; then echo "World가 포함되어 있습니다" fi # 문자열 시작/끝 확인 FILENAME="test.txt" if [[ "$FILENAME" == *.txt ]]; then echo "텍스트 파일입니다" fi if [[ "$FILENAME" == test* ]]; then echo "test로 시작하는 파일입니다" fi
4. 문자열 조작과 비교:
#!/bin/bash # 문자열 길이 확인 TEXT="Hello" LENGTH=${#TEXT} echo "문자열 길이: $LENGTH" if [ $LENGTH -gt 3 ]; then echo "문자열이 3글자보다 깁니다" fi # 부분 문자열 추출 FULL_NAME="홍길동" FIRST_NAME=${FULL_NAME:0:1} # 첫 번째 글자 echo "성: $FIRST_NAME" # 문자열 변환 UPPER_TEXT="HELLO" LOWER_TEXT="hello" if [ "$UPPER_TEXT" = "${LOWER_TEXT^^}" ]; then echo "대문자로 변환하면 같습니다" fi
5. 실용적인 문자열 비교 예시:
#!/bin/bash # 사용자 입력 검증 read -p "사용자명을 입력하세요: " USERNAME # 빈 입력 확인 if [ -z "$USERNAME" ]; then echo "에러: 사용자명을 입력해주세요" exit 1 fi # 길이 확인 if [ ${#USERNAME} -lt 3 ]; then echo "에러: 사용자명은 최소 3글자 이상이어야 합니다" exit 1 fi # 특수문자 확인 (간단한 방법) if [[ "$USERNAME" =~ [^a-zA-Z0-9_] ]]; then echo "에러: 사용자명에는 영문, 숫자, 언더스코어만 사용 가능합니다" exit 1 fi echo "사용자명이 유효합니다: $USERNAME"
파일 존재 확인
파일 확인이란? 파일이나 디렉토리가 존재하는지, 어떤 권한을 가지고 있는지 확인하는 방법입니다.
#!/bin/bash FILE="important.txt" if [ -f "$FILE" ]; then echo "$FILE 파일이 존재합니다." echo "파일 크기: $(ls -lh $FILE | awk '{print $5}')" else echo "$FILE 파일이 없습니다." echo "새 파일을 생성하시겠습니까? (y/n)" read ANSWER if [ "$ANSWER" = "y" ]; then touch "$FILE" echo "파일을 생성했습니다." fi fi
🔍 파일 확인 상세 설명:
1. 기본 파일 확인 연산자:
- -f: 일반 파일인지 확인 -
[ -f “파일명” ]
- -d: 디렉토리인지 확인 -
[ -d “디렉토리명” ]
- -e: 존재하는지 확인 (파일/디렉토리 모두) -
[ -e “경로” ]
- -L: 심볼릭 링크인지 확인 -
[ -L “링크명” ]
2. 권한 확인 연산자:
- -r: 읽기 가능한지 확인 -
[ -r “파일명” ]
- -w: 쓰기 가능한지 확인 -
[ -w “파일명” ]
- -x: 실행 가능한지 확인 -
[ -x “파일명” ]
3. 파일 속성 확인 연산자:
- -s: 파일 크기가 0보다 큰지 확인 -
[ -s “파일명” ]
- -S: 소켓 파일인지 확인 -
[ -S “파일명” ]
- -p: 파이프 파일인지 확인 -
[ -p “파일명” ]
4. 파일 확인 예시:
#!/bin/bash # 파일 존재 확인 if [ -f "test.txt" ]; then echo "test.txt 파일이 존재합니다" else echo "test.txt 파일이 없습니다" fi # 디렉토리 존재 확인 if [ -d "/home/user" ]; then echo "/home/user 디렉토리가 존재합니다" fi # 파일과 디렉토리 구분 if [ -f "$1" ]; then echo "$1은 파일입니다" elif [ -d "$1" ]; then echo "$1은 디렉토리입니다" else echo "$1은 존재하지 않습니다" fi # 권한 확인 if [ -r "config.txt" ]; then echo "config.txt를 읽을 수 있습니다" fi if [ -w "log.txt" ]; then echo "log.txt에 쓸 수 있습니다" fi # 파일 크기 확인 if [ -s "data.txt" ]; then echo "data.txt는 비어있지 않습니다" else echo "data.txt는 비어있습니다" fi
5. 실용적인 파일 처리 예시:
#!/bin/bash # 백업 파일 생성 전 확인 BACKUP_FILE="backup_$(date +%Y%m%d).tar.gz" if [ -f "$BACKUP_FILE" ]; then echo "경고: $BACKUP_FILE이 이미 존재합니다" read -p "덮어쓰시겠습니까? (y/n): " OVERWRITE if [ "$OVERWRITE" != "y" ]; then echo "백업을 취소합니다" exit 1 fi fi # 설정 파일 확인 CONFIG_FILE="/etc/myapp.conf" if [ ! -f "$CONFIG_FILE" ]; then echo "설정 파일이 없습니다: $CONFIG_FILE" echo "기본 설정 파일을 생성합니다" cp /etc/myapp.conf.default "$CONFIG_FILE" fi # 로그 디렉토리 확인 및 생성 LOG_DIR="/var/log/myapp" if [ ! -d "$LOG_DIR" ]; then echo "로그 디렉토리를 생성합니다: $LOG_DIR" mkdir -p "$LOG_DIR" fi # 실행 권한 확인 SCRIPT_FILE="install.sh" if [ -f "$SCRIPT_FILE" ] && [ ! -x "$SCRIPT_FILE" ]; then echo "실행 권한을 부여합니다: $SCRIPT_FILE" chmod +x "$SCRIPT_FILE" fi
6. 파일 정보 수집:
#!/bin/bash # 파일 정보 출력 함수 file_info() { local FILE="$1" if [ ! -e "$FILE" ]; then echo "파일이 존재하지 않습니다: $FILE" return 1 fi echo "=== 파일 정보: $FILE ===" # 파일 타입 if [ -f "$FILE" ]; then echo "타입: 일반 파일" elif [ -d "$FILE" ]; then echo "타입: 디렉토리" elif [ -L "$FILE" ]; then echo "타입: 심볼릭 링크" fi # 파일 크기 if [ -f "$FILE" ]; then SIZE=$(ls -lh "$FILE" | awk '{print $5}') echo "크기: $SIZE" fi # 권한 PERMS=$(ls -l "$FILE" | awk '{print $1}') echo "권한: $PERMS" # 수정 시간 MTIME=$(ls -l "$FILE" | awk '{print $6, $7, $8}') echo "수정시간: $MTIME" } # 사용 예시 file_info "test.txt"
🔄 4단계: 반복문으로 효율성 높이기
for 문 - 목록 반복
반복문이란? 같은 작업을 여러 번 반복해야 할 때 사용하는 프로그래밍 구조입니다.
#!/bin/bash # 파일 목록 반복 echo "현재 폴더의 텍스트 파일들:" for file in *.txt; do if [ -f "$file" ]; then echo "- $file (크기: $(ls -lh "$file" | awk '{print $5}'))" fi done
🔍 for 문 상세 설명:
1. 기본 for 문 구조:
for 변수 in 목록; do # 반복할 명령어들 done
2. 다양한 목록 형태:
#!/bin/bash # 1. 직접 목록 지정 for fruit in "사과" "바나나" "오렌지"; do echo "과일: $fruit" done # 2. 파일 패턴 사용 for file in *.txt; do echo "텍스트 파일: $file" done # 3. 명령어 결과 사용 for user in $(cat users.txt); do echo "사용자: $user" done # 4. 숫자 범위 for i in {1..5}; do echo "숫자: $i" done # 5. 문자 범위 for letter in {a..e}; do echo "문자: $letter" done
3. 숫자 범위 반복:
#!/bin/bash # 기본 범위 for i in {1..10}; do echo "숫자: $i" done # 간격 지정 for i in {0..20..2}; do echo "짝수: $i" done # 역순 for i in {10..1}; do echo "역순: $i" done # C 스타일 for 문 (고급) for ((i=1; i<=10; i++)); do echo "C 스타일: $i" done
4. 파일 처리 예시:
#!/bin/bash # 모든 텍스트 파일 처리 for file in *.txt; do if [ -f "$file" ]; then echo "처리 중: $file" # 파일 처리 작업 wc -l "$file" fi done # 특정 디렉토리의 모든 파일 for file in /home/user/documents/*; do if [ -f "$file" ]; then echo "파일: $file" ls -lh "$file" fi done # 하위 디렉토리까지 모든 파일 (고급) for file in $(find . -name "*.log"); do echo "로그 파일: $file" tail -5 "$file" done
5. 배열과 함께 사용:
#!/bin/bash # 배열 정의 FRUITS=("사과" "바나나" "오렌지" "포도") # 배열 요소 반복 for fruit in "${FRUITS[@]}"; do echo "과일: $fruit" done # 배열 인덱스와 함께 for i in "${!FRUITS[@]}"; do echo "인덱스 $i: ${FRUITS[$i]}" done # 명령줄 인수 반복 for arg in "$@"; do echo "인수: $arg" done
6. 중첩 for 문:
#!/bin/bash # 구구단 예시 for i in {1..9}; do echo "=== $i단 ===" for j in {1..9}; do result=$((i * j)) echo "$i x $j = $result" done echo done # 디렉토리와 파일 반복 for dir in */; do echo "디렉토리: $dir" for file in "$dir"*.txt; do if [ -f "$file" ]; then echo " 파일: $file" fi done done
while 문 - 조건 반복
while 문이란? 특정 조건이 참인 동안 계속 반복하는 반복문입니다.
#!/bin/bash # 사용자가 'quit'을 입력할 때까지 반복 echo "명령어를 입력하세요 (quit으로 종료):" while true; do read -p "> " COMMAND if [ "$COMMAND" = "quit" ]; then echo "프로그램을 종료합니다." break elif [ "$COMMAND" = "date" ]; then date elif [ "$COMMAND" = "pwd" ]; then pwd else echo "알 수 없는 명령어: $COMMAND" fi done
🔍 while 문 상세 설명:
1. 기본 while 문 구조:
while [ 조건 ]; do # 반복할 명령어들 done
2. 다양한 while 문 예시:
#!/bin/bash # 카운터 기반 반복 COUNT=1 while [ $COUNT -le 5 ]; do echo "카운트: $COUNT" COUNT=$((COUNT + 1)) done # 파일 읽기 while read line; do echo "읽은 줄: $line" done < "파일명.txt" # 명령어 결과 읽기 ls -1 | while read file; do echo "파일: $file" done # 조건 기반 반복 PASSWORD="" while [ -z "$PASSWORD" ]; do read -s -p "비밀번호를 입력하세요: " PASSWORD echo done
3. 카운터 기반 반복:
#!/bin/bash # 시스템 상태를 5번 확인 COUNT=1 while [ $COUNT -le 5 ]; do echo "=== $COUNT 번째 확인 ===" echo "현재 시간: $(date)" echo "CPU 사용률: $(top -bn1 | grep "Cpu(s)" | awk '{print $2}' | cut -d'%' -f1)" echo "메모리 사용률: $(free | grep Mem | awk '{printf "%.1f%%", $3/$2 * 100.0}')" echo COUNT=$((COUNT + 1)) sleep 2 done
4. 무한 루프와 제어:
#!/bin/bash # 무한 루프 (조건이 항상 참) while true; do echo "무한 반복 중... (Ctrl+C로 종료)" sleep 1 done # break로 루프 종료 COUNT=1 while true; do echo "카운트: $COUNT" if [ $COUNT -eq 10 ]; then echo "10에 도달했습니다. 종료합니다." break fi COUNT=$((COUNT + 1)) done # continue로 현재 반복 건너뛰기 for i in {1..10}; do if [ $i -eq 5 ]; then echo "5를 건너뜁니다" continue fi echo "숫자: $i" done
5. 파일 처리 while 문:
#!/bin/bash # 파일을 한 줄씩 읽기 while IFS= read -r line; do echo "처리 중: $line" # 각 줄에 대한 처리 작업 done < "input.txt" # 명령어 결과를 한 줄씩 처리 ps aux | while read -r user pid cpu mem vsz rss tty stat start time command; do if [ "$cpu" != "%CPU" ]; then # 헤더 제외 echo "프로세스: $command (CPU: $cpu%)" fi done # 대화형 입력 처리 echo "이름과 나이를 입력하세요 (종료하려면 'quit' 입력):" while true; do read -p "이름: " NAME if [ "$NAME" = "quit" ]; then break fi read -p "나이: " AGE echo "$NAME님은 $AGE세입니다." done
6. 조건 기반 반복:
#!/bin/bash # 파일이 존재할 때까지 대기 while [ ! -f "ready.txt" ]; do echo "ready.txt 파일을 기다리는 중..." sleep 5 done echo "파일이 준비되었습니다!" # 네트워크 연결 확인 while ! ping -c 1 google.com &> /dev/null; do echo "네트워크 연결을 기다리는 중..." sleep 10 done echo "네트워크 연결됨!" # 사용자 입력 검증 while true; do read -p "1-10 사이의 숫자를 입력하세요: " NUMBER if [[ "$NUMBER" =~ ^[0-9]+$ ]] && [ $NUMBER -ge 1 ] && [ $NUMBER -le 10 ]; then echo "올바른 숫자입니다: $NUMBER" break else echo "잘못된 입력입니다. 다시 시도하세요." fi done
🛠️ 5단계: 함수로 코드 재사용하기
함수 정의와 사용
#!/bin/bash # 함수 정의 backup_folder() { local FOLDER=$1 local BACKUP_NAME="backup_$(basename $FOLDER)_$(date +%Y%m%d_%H%M%S).tar.gz" echo "백업 시작: $FOLDER" if tar -czf "$BACKUP_NAME" "$FOLDER"; then echo "백업 성공: $BACKUP_NAME" return 0 else echo "백업 실패: $FOLDER" return 1 fi } # 함수 사용 backup_folder "/home/user/documents" backup_folder "/home/user/pictures"
로그 함수 만들기
#!/bin/bash LOG_FILE="/var/log/my_script.log" # 로그 함수 log_message() { local LEVEL=$1 local MESSAGE=$2 local TIMESTAMP=$(date '+%Y-%m-%d %H:%M:%S') echo "[$TIMESTAMP] [$LEVEL] $MESSAGE" | tee -a "$LOG_FILE" } # 사용 예시 log_message "INFO" "스크립트 시작" log_message "ERROR" "파일을 찾을 수 없습니다" log_message "SUCCESS" "작업 완료"
📋 6단계: 실무 자동화 스크립트 모음
1. 시스템 상태 체크 스크립트
#!/bin/bash # system_check.sh LOG_FILE="/var/log/system_check.log" log_message() { echo "[$(date '+%Y-%m-%d %H:%M:%S')] $1" | tee -a "$LOG_FILE" } log_message "=== 시스템 상태 체크 시작 ===" # CPU 사용률 체크 CPU_USAGE=$(top -bn1 | grep "Cpu(s)" | awk '{print $2}' | cut -d'%' -f1) log_message "CPU 사용률: ${CPU_USAGE}%" if (( $(echo "$CPU_USAGE > 80" | bc -l) )); then log_message "WARNING: CPU 사용률이 높습니다!" fi # 메모리 사용률 체크 MEM_USAGE=$(free | grep Mem | awk '{printf "%.1f", $3/$2 * 100.0}') log_message "메모리 사용률: ${MEM_USAGE}%" if (( $(echo "$MEM_USAGE > 90" | bc -l) )); then log_message "WARNING: 메모리 사용률이 높습니다!" fi # 디스크 사용률 체크 DISK_USAGE=$(df -h / | awk 'NR==2 {print $5}' | cut -d'%' -f1) log_message "디스크 사용률: ${DISK_USAGE}%" if [ "$DISK_USAGE" -gt 85 ]; then log_message "WARNING: 디스크 사용률이 높습니다!" fi log_message "=== 시스템 상태 체크 완료 ==="
2. 자동 백업 스크립트
#!/bin/bash # auto_backup.sh BACKUP_SOURCE="/home/user/important_data" BACKUP_DEST="/backup" BACKUP_NAME="backup_$(date +%Y%m%d_%H%M%S).tar.gz" MAX_BACKUPS=7 # 백업 디렉토리 생성 mkdir -p "$BACKUP_DEST" # 백업 실행 echo "백업 시작: $(date)" if tar -czf "$BACKUP_DEST/$BACKUP_NAME" "$BACKUP_SOURCE"; then echo "백업 성공: $BACKUP_NAME" # 오래된 백업 파일 삭제 (최근 7개만 유지) cd "$BACKUP_DEST" ls -t backup_*.tar.gz | tail -n +$((MAX_BACKUPS + 1)) | xargs -r rm echo "백업 완료: $(date)" else echo "백업 실패: $(date)" exit 1 fi
3. 로그 분석 스크립트
#!/bin/bash # log_analyzer.sh LOG_FILE="/var/log/syslog" REPORT_FILE="/tmp/log_report_$(date +%Y%m%d).txt" echo "=== 로그 분석 리포트 ($(date)) ===" > "$REPORT_FILE" echo >> "$REPORT_FILE" # 에러 개수 세기 ERROR_COUNT=$(grep -i "error" "$LOG_FILE" | wc -l) echo "총 에러 개수: $ERROR_COUNT" >> "$REPORT_FILE" # 가장 많이 발생한 에러 TOP 5 echo >> "$REPORT_FILE" echo "=== 가장 많이 발생한 에러 TOP 5 ===" >> "$REPORT_FILE" grep -i "error" "$LOG_FILE" | awk '{print $5}' | sort | uniq -c | sort -nr | head -5 >> "$REPORT_FILE" # 시간대별 에러 분포 echo >> "$REPORT_FILE" echo "=== 시간대별 에러 분포 ===" >> "$REPORT_FILE" grep -i "error" "$LOG_FILE" | awk '{print $3}' | cut -d: -f1 | sort | uniq -c | sort -nr >> "$REPORT_FILE" echo "로그 분석 완료: $REPORT_FILE"
4. 웹사이트 상태 모니터링 스크립트
#!/bin/bash # website_monitor.sh WEBSITES=("https://google.com" "https://github.com" "https://stackoverflow.com") LOG_FILE="/var/log/website_monitor.log" log_message() { echo "[$(date '+%Y-%m-%d %H:%M:%S')] $1" | tee -a "$LOG_FILE" } for website in "${WEBSITES[@]}"; do log_message "체크 중: $website" # HTTP 상태 코드 확인 STATUS_CODE=$(curl -s -o /dev/null -w "%{http_code}" "$website") if [ "$STATUS_CODE" -eq 200 ]; then log_message "✅ $website - 정상 (HTTP $STATUS_CODE)" else log_message "❌ $website - 문제 발생 (HTTP $STATUS_CODE)" # 이메일 알림 (선택사항) # echo "웹사이트 문제: $website (HTTP $STATUS_CODE)" | mail -s "웹사이트 알림" [email protected] fi done
⏰ 7단계: 크론탭으로 자동 실행하기
크론탭이 뭔가요?
크론탭(crontab)은 Linux에서 정해진 시간에 자동으로 스크립트를 실행해주는 스케줄러입니다.
크론탭 설정하기
# 크론탭 편집 crontab -e # 현재 크론탭 확인 crontab -l # 크론탭 삭제 crontab -r
크론탭 시간 형식
분 시 일 월 요일 명령어 * * * * * /path/to/script.sh # 예시: # 매일 오전 2시에 백업 실행 0 2 * * * /home/user/backup.sh # 매주 월요일 오전 9시에 시스템 체크 0 9 * * 1 /home/user/system_check.sh # 매 5분마다 웹사이트 모니터링 */5 * * * * /home/user/website_monitor.sh # 매월 1일 오전 3시에 로그 정리 0 3 1 * * /home/user/cleanup_logs.sh
시간 설정 도우미:
- *
: 모든 값
- */5
: 5분마다
- 0-23
: 0시부터 23시까지
- 1,15
: 1일과 15일
- 1-5
: 월요일부터 금요일까지
실용적인 크론탭 예시
# 매일 오전 2시 - 자동 백업 0 2 * * * /home/user/scripts/backup.sh >> /var/log/backup.log 2>&1 # 매 10분마다 - 시스템 상태 체크 */10 * * * * /home/user/scripts/system_check.sh # 매주 일요일 오전 3시 - 로그 정리 0 3 * * 0 /home/user/scripts/cleanup.sh # 매월 1일 오전 4시 - 월간 리포트 생성 0 4 1 * * /home/user/scripts/monthly_report.sh # 평일 오전 9시 - 업무 시작 알림 0 9 * * 1-5 /home/user/scripts/work_start_notification.sh
🔧 8단계: 디버깅과 에러 처리
스크립트 디버깅
#!/bin/bash # debug_example.sh # 디버그 모드 켜기 set -x # 실행되는 명령어 모두 출력 set -e # 에러 발생 시 즉시 종료 echo "디버깅 시작" ls -la echo "디버깅 끝" # 디버그 모드 끄기 set +x
디버깅 옵션:
- set -x
: 실행 명령어 출력
- set -e
: 에러 시 즉시 종료
- set -u
: 정의되지 않은 변수 사용 시 에러
에러 처리하기
#!/bin/bash # error_handling.sh # 에러 처리 함수 handle_error() { echo "에러 발생: 줄 번호 $1" echo "마지막 명령어: $2" exit 1 } # 에러 트랩 설정 trap 'handle_error $LINENO "$BASH_COMMAND"' ERR echo "작업 시작" # 존재하지 않는 파일 복사 시도 (에러 발생) cp nonexistent.txt backup.txt echo "이 줄은 실행되지 않습니다"
안전한 스크립트 작성
#!/bin/bash # safe_script.sh # 안전한 스크립트 설정 set -euo pipefail # 변수 초기화 SOURCE_DIR="" DEST_DIR="" # 도움말 함수 show_help() { echo "사용법: $0 -s <원본디렉토리> -d <대상디렉토리>" echo " -s: 원본 디렉토리 경로" echo " -d: 대상 디렉토리 경로" echo " -h: 도움말 출력" } # 명령줄 옵션 처리 while getopts "s:d:h" opt; do case $opt in s) SOURCE_DIR="$OPTARG" ;; d) DEST_DIR="$OPTARG" ;; h) show_help; exit 0 ;; *) show_help; exit 1 ;; esac done # 필수 인수 확인 if [[ -z "$SOURCE_DIR" || -z "$DEST_DIR" ]]; then echo "에러: 원본과 대상 디렉토리를 모두 지정해야 합니다." show_help exit 1 fi # 디렉토리 존재 확인 if [[ ! -d "$SOURCE_DIR" ]]; then echo "에러: 원본 디렉토리가 존재하지 않습니다: $SOURCE_DIR" exit 1 fi # 작업 실행 echo "백업 시작: $SOURCE_DIR -> $DEST_DIR" mkdir -p "$DEST_DIR" cp -r "$SOURCE_DIR"/* "$DEST_DIR"/ echo "백업 완료"
🎯 실전 프로젝트: 종합 서버 관리 스크립트
#!/bin/bash # server_manager.sh - 종합 서버 관리 스크립트 # 설정 LOG_FILE="/var/log/server_manager.log" BACKUP_DIR="/backup" ALERT_EMAIL="[email protected]" CPU_THRESHOLD=80 MEM_THRESHOLD=90 DISK_THRESHOLD=85 # 로그 함수 log() { echo "[$(date '+%Y-%m-%d %H:%M:%S')] $1" | tee -a "$LOG_FILE" } # 시스템 상태 체크 check_system() { log "=== 시스템 상태 체크 시작 ===" # CPU 체크 CPU_USAGE=$(top -bn1 | grep "Cpu(s)" | awk '{print $2}' | cut -d'%' -f1) log "CPU 사용률: ${CPU_USAGE}%" if (( $(echo "$CPU_USAGE > $CPU_THRESHOLD" | bc -l) )); then log "WARNING: CPU 사용률이 임계값을 초과했습니다!" send_alert "CPU 사용률 경고: ${CPU_USAGE}%" fi # 메모리 체크 MEM_USAGE=$(free | grep Mem | awk '{printf "%.1f", $3/$2 * 100.0}') log "메모리 사용률: ${MEM_USAGE}%" if (( $(echo "$MEM_USAGE > $MEM_THRESHOLD" | bc -l) )); then log "WARNING: 메모리 사용률이 임계값을 초과했습니다!" send_alert "메모리 사용률 경고: ${MEM_USAGE}%" fi # 디스크 체크 DISK_USAGE=$(df -h / | awk 'NR==2 {print $5}' | cut -d'%' -f1) log "디스크 사용률: ${DISK_USAGE}%" if [ "$DISK_USAGE" -gt "$DISK_THRESHOLD" ]; then log "WARNING: 디스크 사용률이 임계값을 초과했습니다!" send_alert "디스크 사용률 경고: ${DISK_USAGE}%" fi log "=== 시스템 상태 체크 완료 ===" } # 백업 실행 run_backup() { log "=== 백업 시작 ===" BACKUP_NAME="server_backup_$(date +%Y%m%d_%H%M%S).tar.gz" BACKUP_PATH="$BACKUP_DIR/$BACKUP_NAME" mkdir -p "$BACKUP_DIR" if tar -czf "$BACKUP_PATH" /etc /home /var/www 2>/dev/null; then log "백업 성공: $BACKUP_NAME" # 오래된 백업 파일 정리 (7일 이상) find "$BACKUP_DIR" -name "server_backup_*.tar.gz" -mtime +7 -delete log "오래된 백업 파일 정리 완료" else log "ERROR: 백업 실패" send_alert "서버 백업 실패" fi log "=== 백업 완료 ===" } # 로그 정리 cleanup_logs() { log "=== 로그 정리 시작 ===" # 30일 이상 된 로그 파일 압축 find /var/log -name "*.log" -mtime +30 -exec gzip {} \; # 90일 이상 된 압축 로그 파일 삭제 find /var/log -name "*.log.gz" -mtime +90 -delete log "로그 정리 완료" } # 알림 전송 send_alert() { local MESSAGE="$1" log "알림 전송: $MESSAGE" # 이메일 전송 (mail 명령어가 설정되어 있는 경우) if command -v mail &> /dev/null; then echo "$MESSAGE" | mail -s "서버 알림" "$ALERT_EMAIL" fi } # 메인 함수 main() { case "$1" in "check") check_system ;; "backup") run_backup ;; "cleanup") cleanup_logs ;; "all") check_system run_backup cleanup_logs ;; *) echo "사용법: $0 {check|backup|cleanup|all}" echo " check - 시스템 상태 체크" echo " backup - 백업 실행" echo " cleanup - 로그 정리" echo " all - 모든 작업 실행" exit 1 ;; esac } # 스크립트 실행 main "$@"
🎉 축하합니다!
쉘 스크립트 마스터가 되셨습니다! 🎊
이제 할 수 있는 것들: - ✅ 반복 작업을 자동화하는 스크립트 작성 - ✅ 조건문과 반복문을 활용한 논리적 프로그래밍 - ✅ 시스템 모니터링 및 백업 자동화 - ✅ 크론탭으로 정기 작업 스케줄링 - ✅ 에러 처리와 디버깅이 가능한 안전한 스크립트
실무 활용 팁: - 작은 스크립트부터 시작해서 점진적으로 확장하세요 - 항상 백업과 테스트를 먼저 해보세요 - 로그를 남기고 모니터링하는 습관을 기르세요 - 다른 사람이 이해할 수 있도록 주석을 달아주세요
🎓 다음 단계: 이제 실제 업무에 적용해보세요!
📚 참고자료
- Bash 스크립트 가이드: https://www.gnu.org/software/bash/manual/
- 쉘 스크립트 튜토리얼: https://www.shellscript.sh/
- 크론탭 생성기: https://crontab.guru/
- 정규표현식 연습: https://regexr.com/
💬 도움이 필요하면:
man bash
- Bash 매뉴얼help 명령어
- 내장 명령어 도움말명령어 –help
- 명령어별 도움말
💡 자동화의 철학: “게으른 사람이 최고의 프로그래머다” - 반복 작업을 자동화하여 더 중요한 일에 집중하세요!