사용자 도구

사이트 도구


wiki:it:linux:shell_scripting

쉘 스크립트로 업무 자동화

🎯 목표: 반복 작업을 자동화하여 생산성을 극대화하기

⏰ 예상 시간: 3-4시간

📋 준비물: - 기본 명령어와 고급 명령어에 익숙한 상태 - 터미널 접근 권한 - 텍스트 에디터 (nano, vim 등)

반복 작업에 지치셨나요? 쉘 스크립트로 모든 걸 자동화해보세요! 한 번 만들어두면 평생 써먹을 수 있는 자동화 스크립트를 만들어보겠습니다.

🎯 학습 목표

이 가이드를 마치면:

  1. 쉘 스크립트의 기본 문법을 이해할 수 있습니다
  2. 조건문과 반복문을 활용한 논리적 프로그래밍이 가능합니다
  3. 실무에서 바로 쓸 수 있는 자동화 스크립트를 만들 수 있습니다
  4. 크론탭으로 정기적으로 실행되는 작업을 설정할 수 있습니다

🚀 1단계: 쉘 스크립트 첫 만남

쉘 스크립트가 뭔가요?

쉘 스크립트는 Linux 명령어들을 파일에 저장해서 한 번에 실행할 수 있게 해주는 프로그램입니다.

예시:

# 매일 하는 작업들
pwd
ls -la
df -h
ps aux | grep apache

이런 명령어들을 매번 타이핑하는 대신, 스크립트 파일에 저장해두고 한 번에 실행할 수 있습니다!

쉘 스크립트의 기본 구조

1. 셔뱅(Shebang) - 필수!

#!/bin/bash

- 의미: 이 파일이 bash 쉘로 실행되어야 함을 알림 - 위치: 파일의 첫 번째 줄에 반드시 작성 - 다른 쉘들:

  1. #!/bin/sh - 기본 쉘
  2. #!/usr/bin/python3 - Python 스크립트
  3. #!/usr/bin/perl - Perl 스크립트

2. 주석(Comment) - 설명을 위한 메모

# 이것은 한 줄 주석입니다
echo "Hello World"  # 이 줄 끝에도 주석 가능

# 여러 줄 주석은 이렇게
# 각 줄마다 #을 붙여야 합니다

3. 명령어 실행

# 각 줄은 하나의 명령어
echo "첫 번째 명령어"
ls -la
pwd

4. 스크립트 종료

  1. 스크립트는 마지막 명령어까지 실행 후 자동 종료
  2. exit 0 - 정상 종료 (선택사항)
  3. exit 1 - 에러로 종료 (선택사항)

첫 번째 스크립트 만들기

1. 스크립트 파일 생성:

nano hello.sh

2. 스크립트 내용 작성:

#!/bin/bash
# 이것은 주석입니다
echo "안녕하세요! 첫 번째 쉘 스크립트입니다."
echo "현재 시간: $(date)"
echo "현재 위치: $(pwd)"

3. 실행 권한 부여:

chmod +x hello.sh

4. 스크립트 실행:

./hello.sh

🔍 이해하기:

  1. #!/bin/bash: 쉘 스크립트임을 알리는 셔뱅(shebang)
  2. #: 주석 (설명)
  3. echo: 텍스트 출력
  4. $(명령어): 명령어 실행 결과를 변수로 사용

💡 초보자 팁:

  1. 스크립트 파일명은 .sh 확장자를 붙이는 것이 관례
  2. 파일명에 공백이나 특수문자 사용 금지
  3. 실행 권한은 한 번만 부여하면 됨 (chmod +x)
  4. 스크립트 실행 시 ./를 앞에 붙여야 함

📝 2단계: 변수와 사용자 입력

변수 사용하기

변수란? 데이터를 저장하는 상자라고 생각하세요!

#!/bin/bash

# 변수 선언 (= 앞뒤에 공백 없이!)
NAME="홍길동"
AGE=25
TODAY=$(date +%Y-%m-%d)

# 변수 사용 ($ 붙이기)
echo "이름: $NAME"
echo "나이: $AGE"
echo "오늘 날짜: $TODAY"

# 중괄호로 변수 구분하기
echo "안녕하세요 ${NAME}님!"

🔍 변수 문법 상세 설명:

1. 변수 선언 규칙:

  1. 공백 금지: NAME=“홍길동” (O), NAME = “홍길동” (X)
  2. 따옴표 사용: 공백이 있는 값은 따옴표로 감싸기
  3. 숫자는 따옴표 없이: AGE=25 (O), AGE=“25” (O, 하지만 불필요)

2. 변수 사용 방법:

  1. 기본: $변수명 - echo $NAME
  2. 중괄호: ${변수명} - echo ${NAME}님
  3. 중괄호가 필요한 경우:
    # 변수명이 명확하지 않을 때
    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. 옵션들:

  1. -p “메시지”: 프롬프트 메시지와 함께 입력받기
  2. -s: 비밀번호처럼 입력 내용을 화면에 표시하지 않기
  3. -t 초: 시간 제한 (초 단위)
  4. -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. 특별한 변수들:

  1. $0: 스크립트 파일명
  2. $1, $2, $3…: 첫 번째, 두 번째, 세 번째 인수
  3. $@: 모든 인수를 배열로
  4. $#: 인수의 개수
  5. $*: 모든 인수를 하나의 문자열로

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. 조건 연산자 (숫자 비교):

  1. -eq: 같음 (equal) - [ $a -eq $b ]
  2. -ne: 다름 (not equal) - [ $a -ne $b ]
  3. -gt: 큼 (greater than) - [ $a -gt $b ]
  4. -ge: 크거나 같음 (greater equal) - [ $a -ge $b ]
  5. -lt: 작음 (less than) - [ $a -lt $b ]
  6. -le: 작거나 같음 (less equal) - [ $a -le $b ]

3. 조건 연산자 (문자열 비교):

  1. =: 같음 - [ “$a” = “$b” ]
  2. !=: 다름 - [ “$a” != “$b” ]
  3. -z: 빈 문자열인지 확인 - [ -z “$a” ]
  4. -n: 빈 문자열이 아닌지 확인 - [ -n “$a” ]

4. 조건 연산자 (파일/디렉토리):

  1. -f: 파일인지 확인 - [ -f “파일명” ]
  2. -d: 디렉토리인지 확인 - [ -d “디렉토리명” ]
  3. -e: 존재하는지 확인 - [ -e “파일명” ]
  4. -r: 읽기 가능한지 확인 - [ -r “파일명” ]
  5. -w: 쓰기 가능한지 확인 - [ -w “파일명” ]
  6. -x: 실행 가능한지 확인 - [ -x “파일명” ]

5. 논리 연산자:

  1. &&: AND (그리고) - [ 조건1 ] && [ 조건2 ]
  2. ||: OR (또는) - [ 조건1 ] || [ 조건2 ]
  3. !: 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. 기본 비교 연산자:

  1. =: 같음 - [ “$a” = “$b” ]
  2. !=: 다름 - [ “$a” != “$b” ]
  3. -z: 빈 문자열인지 확인 - [ -z “$a” ]
  4. -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. 기본 파일 확인 연산자:

  1. -f: 일반 파일인지 확인 - [ -f “파일명” ]
  2. -d: 디렉토리인지 확인 - [ -d “디렉토리명” ]
  3. -e: 존재하는지 확인 (파일/디렉토리 모두) - [ -e “경로” ]
  4. -L: 심볼릭 링크인지 확인 - [ -L “링크명” ]

2. 권한 확인 연산자:

  1. -r: 읽기 가능한지 확인 - [ -r “파일명” ]
  2. -w: 쓰기 가능한지 확인 - [ -w “파일명” ]
  3. -x: 실행 가능한지 확인 - [ -x “파일명” ]

3. 파일 속성 확인 연산자:

  1. -s: 파일 크기가 0보다 큰지 확인 - [ -s “파일명” ]
  2. -S: 소켓 파일인지 확인 - [ -S “파일명” ]
  3. -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 "$@"

🎉 축하합니다!

쉘 스크립트 마스터가 되셨습니다! 🎊

이제 할 수 있는 것들: - ✅ 반복 작업을 자동화하는 스크립트 작성 - ✅ 조건문과 반복문을 활용한 논리적 프로그래밍 - ✅ 시스템 모니터링 및 백업 자동화 - ✅ 크론탭으로 정기 작업 스케줄링 - ✅ 에러 처리와 디버깅이 가능한 안전한 스크립트

실무 활용 팁: - 작은 스크립트부터 시작해서 점진적으로 확장하세요 - 항상 백업과 테스트를 먼저 해보세요 - 로그를 남기고 모니터링하는 습관을 기르세요 - 다른 사람이 이해할 수 있도록 주석을 달아주세요

🎓 다음 단계: 이제 실제 업무에 적용해보세요!

📚 참고자료

  1. Bash 스크립트 가이드: https://www.gnu.org/software/bash/manual/
  2. 쉘 스크립트 튜토리얼: https://www.shellscript.sh/
  3. 크론탭 생성기: https://crontab.guru/
  4. 정규표현식 연습: https://regexr.com/

💬 도움이 필요하면:

  1. man bash - Bash 매뉴얼
  2. help 명령어 - 내장 명령어 도움말
  3. 명령어 –help - 명령어별 도움말

💡 자동화의 철학: “게으른 사람이 최고의 프로그래머다” - 반복 작업을 자동화하여 더 중요한 일에 집중하세요!

wiki/it/linux/shell_scripting.txt · 마지막으로 수정됨: 저자 127.0.0.1

Donate Powered by PHP Valid HTML5 Valid CSS Driven by DokuWiki